From 811498cd384dda13b953fce4b41bad92f4ea53d2 Mon Sep 17 00:00:00 2001 From: Lewis Date: Sun, 18 May 2025 15:02:45 -0700 Subject: [PATCH 1/3] feat(kotlin): add logic & metadata for publishing --- kotlin/README.md | 3 + kotlin/gradle.properties | 3 +- kotlin/gradle/libs.versions.toml | 2 + kotlin/lib/build.gradle.kts | 113 ++++++++++++++++++++++++++++++- 4 files changed, 119 insertions(+), 2 deletions(-) create mode 100644 kotlin/README.md diff --git a/kotlin/README.md b/kotlin/README.md new file mode 100644 index 00000000..333bf749 --- /dev/null +++ b/kotlin/README.md @@ -0,0 +1,3 @@ +# Module Iroh + +A toolkit for building distributed applications \ No newline at end of file diff --git a/kotlin/gradle.properties b/kotlin/gradle.properties index 377538c9..8e704cec 100644 --- a/kotlin/gradle.properties +++ b/kotlin/gradle.properties @@ -2,4 +2,5 @@ # https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties org.gradle.configuration-cache=true - +org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true diff --git a/kotlin/gradle/libs.versions.toml b/kotlin/gradle/libs.versions.toml index 2660e408..e03dd736 100644 --- a/kotlin/gradle/libs.versions.toml +++ b/kotlin/gradle/libs.versions.toml @@ -13,3 +13,5 @@ junit-jupiter-engine = { module = "org.junit.jupiter:junit-jupiter-engine", vers [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version = "2.0.20" } +dokka = { id = "org.jetbrains.dokka", version = "2.0.0" } + diff --git a/kotlin/lib/build.gradle.kts b/kotlin/lib/build.gradle.kts index e815d813..644c9179 100644 --- a/kotlin/lib/build.gradle.kts +++ b/kotlin/lib/build.gradle.kts @@ -1,4 +1,3 @@ - /* * This file was generated by the Gradle 'init' task. * @@ -12,8 +11,15 @@ plugins { // Apply the java-library plugin for API and implementation separation. `java-library` + + `maven-publish` + alias(libs.plugins.dokka) + signing } +group = "computer.iroh" +version = "0.31.0" + repositories { // Use Maven Central for resolving dependencies. mavenCentral() @@ -35,6 +41,8 @@ dependencies { // Apply a specific Java toolchain to ease working on different environments. java { + withJavadocJar() + withSourcesJar() toolchain { languageVersion = JavaLanguageVersion.of(21) } @@ -57,3 +65,106 @@ tasks.named("test") { events("passed", "skipped", "failed") } } + +val stagingDir = layout.buildDirectory.dir("staging") + +signing { + // Use property `signing.keyId` to pick GPG signing key + // e.g. `./gradlew publish -Psigning.keyId=24875D73` + // Or, place it in `~/.gradle/gradle.properties` for persistence. + + useGpgCmd() + sign(publishing.publications) +} + +dokka { + moduleName = "Iroh" + dokkaSourceSets.main { + includes.from(rootProject.projectDir.resolve("README.md")) + sourceLink { + localDirectory = file("src/main/kotlin") + remoteUrl("https://github.com/n0-computer/iroh-ffi/blob/main/kotlin/lib/src/main/kotlin") + remoteLineSuffix.set("#L") + } + } + pluginsConfiguration.html { + footerMessage.set("© n0, inc.") + } + // Runs Dokka in the current Gradle process + dokkaGeneratorIsolation = ClassLoaderIsolation() +} + +tasks.named("javadocJar", Jar::class) { + dependsOn(tasks.dokkaGenerate) + from(layout.buildDirectory.dir("dokka/html")) +} + +publishing { + publications { + register("maven") { + groupId = project.group.toString() + version = project.version.toString() + artifactId = "iroh" + + pom { + // https://central.sonatype.org/publish/requirements/#required-pom-metadata + + name = "Iroh" + description = "A toolkit for building distributed applications" + inceptionYear = "2025" + url = "https://iroh.computer" + + developers { + developer { + id = "number0" + email = "hello@n0.computer" + organization = "number0" + organizationUrl = "https://n0.computer" + } + } + + licenses { + license { + name = "Apache License, Version 2.0" + url = "https://opensource.org/license/apache-2-0" + } + license { + name = "The MIT License" + url = "https://opensource.org/licenses/MIT" + } + } + + scm { + connection = "scm:git:git://https://github.com/n0-computer/iroh-ffi.git" + developerConnection = "scm:git:ssh://https://github.com/n0-computer/iroh-ffi.git" + url = "https://github.com/n0-computer/iroh-ffi" + } + } + + from(components["java"]) + } + } + + repositories { + maven(uri(stagingDir)) { + name = "staging" + } + } +} + +val cleanStagingDir by tasks.register("cleanStagingDir") { + delete(stagingDir) +} + +val zipStagingDir by tasks.register("zipStagingDir") { + from(stagingDir) + archiveFileName.set("deploy.jar") +} + +tasks.named("publishMavenPublicationToStagingRepository") { + dependsOn(cleanStagingDir) +} + +tasks.publish { + finalizedBy(zipStagingDir) +} From 122dd1ee94e8eb73fe03238e7b440f0d16cee404 Mon Sep 17 00:00:00 2001 From: Lewis Date: Sun, 18 May 2025 15:29:31 -0700 Subject: [PATCH 2/3] fix(kotlin): use jar task to create final jar --- kotlin/lib/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kotlin/lib/build.gradle.kts b/kotlin/lib/build.gradle.kts index 644c9179..90fc6625 100644 --- a/kotlin/lib/build.gradle.kts +++ b/kotlin/lib/build.gradle.kts @@ -156,7 +156,7 @@ val cleanStagingDir by tasks.register("cleanStagingDir") { delete(stagingDir) } -val zipStagingDir by tasks.register("zipStagingDir") { +val stagingDirJar by tasks.register("stagingDirJar") { from(stagingDir) archiveFileName.set("deploy.jar") } @@ -166,5 +166,5 @@ tasks.named("publishMavenPublicationToStagingRepository") { } tasks.publish { - finalizedBy(zipStagingDir) + finalizedBy(stagingDirJar) } From 578cfd000bb9928ccb0442a5f23230843defef76 Mon Sep 17 00:00:00 2001 From: Lewis Date: Mon, 16 Jun 2025 20:53:12 -0700 Subject: [PATCH 3/3] refactor(kotlin): move codegen logic into gradle This should help to improve caching and keeps some volatile generated code out of version control. --- kotlin/lib/build.gradle.kts | 46 + .../src/main/kotlin/computer/iroh/iroh_ffi.kt | 28180 ---------------- make_kotlin.sh | 11 +- 3 files changed, 50 insertions(+), 28187 deletions(-) delete mode 100644 kotlin/lib/src/main/kotlin/computer/iroh/iroh_ffi.kt diff --git a/kotlin/lib/build.gradle.kts b/kotlin/lib/build.gradle.kts index 90fc6625..d74fb16e 100644 --- a/kotlin/lib/build.gradle.kts +++ b/kotlin/lib/build.gradle.kts @@ -54,6 +54,52 @@ kotlin { } } +val nativeLibraryDir = rootDir.resolve("..").normalize() +val profile = providers.gradleProperty("profile").orElse("release") + +val buildNativeLibraryTask by tasks.register("buildNativeLibrary") { + inputs.dir(nativeLibraryDir.resolve("src")) + inputs.files( + nativeLibraryDir.resolve("Cargo.toml"), + nativeLibraryDir.resolve("Cargo.lock"), + nativeLibraryDir.resolve("build.rs"), + ) + outputs.dir(nativeLibraryDir.resolve("target")) + + workingDir = nativeLibraryDir + commandLine = listOf( + "cargo", "build", + "--lib", "--profile", profile.get(), + "--color", "always", + ) +} + +val generateNativeBindingsTask by tasks.register("generateNativeBindings") { + dependsOn(buildNativeLibraryTask) + + val libName = System.mapLibraryName("iroh_ffi") + val libFile = nativeLibraryDir.resolve("target/${profile.get()}/$libName") + val generatedFFIDir = layout.buildDirectory.dir("generated/uniffi/main/kotlin") + + inputs.file(libFile) + outputs.dir(generatedFFIDir) + + workingDir = nativeLibraryDir + commandLine = listOf( + "cargo", "run", "--bin", "uniffi-bindgen", + "generate", + "--language", "kotlin", + "--out-dir", generatedFFIDir.get().asFile.absolutePath, + "--config", "uniffi.toml", + "--library", libFile.absolutePath, + "--no-format", // roughly 60x speedup + ) +} + +sourceSets.main { + kotlin.srcDir(generateNativeBindingsTask) +} + tasks.named("test") { // Use JUnit Platform for unit tests. useJUnitPlatform() diff --git a/kotlin/lib/src/main/kotlin/computer/iroh/iroh_ffi.kt b/kotlin/lib/src/main/kotlin/computer/iroh/iroh_ffi.kt deleted file mode 100644 index 3eee5405..00000000 --- a/kotlin/lib/src/main/kotlin/computer/iroh/iroh_ffi.kt +++ /dev/null @@ -1,28180 +0,0 @@ -// This file was autogenerated by some hot garbage in the `uniffi` crate. -// Trust me, you don't want to mess with it! - -@file:Suppress("NAME_SHADOWING") - -package computer.iroh - -// Common helper code. -// -// Ideally this would live in a separate .kt file where it can be unittested etc -// in isolation, and perhaps even published as a re-useable package. -// -// However, it's important that the details of how this helper code works (e.g. the -// way that different builtin types are passed across the FFI) exactly match what's -// expected by the Rust code on the other side of the interface. In practice right -// now that means coming from the exact some version of `uniffi` that was used to -// compile the Rust component. The easiest way to ensure this is to bundle the Kotlin -// helpers directly inline like we're doing here. - -import com.sun.jna.Callback -import com.sun.jna.Library -import com.sun.jna.Native -import com.sun.jna.Pointer -import com.sun.jna.Structure -import com.sun.jna.ptr.* -import kotlinx.coroutines.CancellableContinuation -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import kotlinx.coroutines.suspendCancellableCoroutine -import java.nio.ByteBuffer -import java.nio.ByteOrder -import java.nio.CharBuffer -import java.nio.charset.CodingErrorAction -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.atomic.AtomicBoolean -import java.util.concurrent.atomic.AtomicLong -import kotlin.coroutines.resume - -// This is a helper for safely working with byte buffers returned from the Rust code. -// A rust-owned buffer is represented by its capacity, its current length, and a -// pointer to the underlying data. - -/** - * @suppress - */ -@Structure.FieldOrder("capacity", "len", "data") -open class RustBuffer : Structure() { - // Note: `capacity` and `len` are actually `ULong` values, but JVM only supports signed values. - // When dealing with these fields, make sure to call `toULong()`. - @JvmField var capacity: Long = 0 - - @JvmField var len: Long = 0 - - @JvmField var data: Pointer? = null - - class ByValue : - RustBuffer(), - Structure.ByValue - - class ByReference : - RustBuffer(), - Structure.ByReference - - internal fun setValue(other: RustBuffer) { - capacity = other.capacity - len = other.len - data = other.data - } - - companion object { - internal fun alloc(size: ULong = 0UL) = - uniffiRustCall { status -> - // Note: need to convert the size to a `Long` value to make this work with JVM. - UniffiLib.INSTANCE.ffi_iroh_ffi_rustbuffer_alloc(size.toLong(), status) - }.also { - if (it.data == null) { - throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=$size)") - } - } - - internal fun create( - capacity: ULong, - len: ULong, - data: Pointer?, - ): RustBuffer.ByValue { - var buf = RustBuffer.ByValue() - buf.capacity = capacity.toLong() - buf.len = len.toLong() - buf.data = data - return buf - } - - internal fun free(buf: RustBuffer.ByValue) = - uniffiRustCall { status -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rustbuffer_free(buf, status) - } - } - - @Suppress("TooGenericExceptionThrown") - fun asByteBuffer() = - this.data?.getByteBuffer(0, this.len.toLong())?.also { - it.order(ByteOrder.BIG_ENDIAN) - } -} - -/** - * The equivalent of the `*mut RustBuffer` type. - * Required for callbacks taking in an out pointer. - * - * Size is the sum of all values in the struct. - * - * @suppress - */ -class RustBufferByReference : ByReference(16) { - /** - * Set the pointed-to `RustBuffer` to the given value. - */ - fun setValue(value: RustBuffer.ByValue) { - // NOTE: The offsets are as they are in the C-like struct. - val pointer = getPointer() - pointer.setLong(0, value.capacity) - pointer.setLong(8, value.len) - pointer.setPointer(16, value.data) - } - - /** - * Get a `RustBuffer.ByValue` from this reference. - */ - fun getValue(): RustBuffer.ByValue { - val pointer = getPointer() - val value = RustBuffer.ByValue() - value.writeField("capacity", pointer.getLong(0)) - value.writeField("len", pointer.getLong(8)) - value.writeField("data", pointer.getLong(16)) - - return value - } -} - -// This is a helper for safely passing byte references into the rust code. -// It's not actually used at the moment, because there aren't many things that you -// can take a direct pointer to in the JVM, and if we're going to copy something -// then we might as well copy it into a `RustBuffer`. But it's here for API -// completeness. - -@Structure.FieldOrder("len", "data") -internal open class ForeignBytes : Structure() { - @JvmField var len: Int = 0 - - @JvmField var data: Pointer? = null - - class ByValue : - ForeignBytes(), - Structure.ByValue -} - -/** - * The FfiConverter interface handles converter types to and from the FFI - * - * All implementing objects should be public to support external types. When a - * type is external we need to import it's FfiConverter. - * - * @suppress - */ -public interface FfiConverter { - // Convert an FFI type to a Kotlin type - fun lift(value: FfiType): KotlinType - - // Convert an Kotlin type to an FFI type - fun lower(value: KotlinType): FfiType - - // Read a Kotlin type from a `ByteBuffer` - fun read(buf: ByteBuffer): KotlinType - - // Calculate bytes to allocate when creating a `RustBuffer` - // - // This must return at least as many bytes as the write() function will - // write. It can return more bytes than needed, for example when writing - // Strings we can't know the exact bytes needed until we the UTF-8 - // encoding, so we pessimistically allocate the largest size possible (3 - // bytes per codepoint). Allocating extra bytes is not really a big deal - // because the `RustBuffer` is short-lived. - fun allocationSize(value: KotlinType): ULong - - // Write a Kotlin type to a `ByteBuffer` - fun write( - value: KotlinType, - buf: ByteBuffer, - ) - - // Lower a value into a `RustBuffer` - // - // This method lowers a value into a `RustBuffer` rather than the normal - // FfiType. It's used by the callback interface code. Callback interface - // returns are always serialized into a `RustBuffer` regardless of their - // normal FFI type. - fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue { - val rbuf = RustBuffer.alloc(allocationSize(value)) - try { - val bbuf = - rbuf.data!!.getByteBuffer(0, rbuf.capacity).also { - it.order(ByteOrder.BIG_ENDIAN) - } - write(value, bbuf) - rbuf.writeField("len", bbuf.position().toLong()) - return rbuf - } catch (e: Throwable) { - RustBuffer.free(rbuf) - throw e - } - } - - // Lift a value from a `RustBuffer`. - // - // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. - // It's currently only used by the `FfiConverterRustBuffer` class below. - fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType { - val byteBuf = rbuf.asByteBuffer()!! - try { - val item = read(byteBuf) - if (byteBuf.hasRemaining()) { - throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") - } - return item - } finally { - RustBuffer.free(rbuf) - } - } -} - -/** - * FfiConverter that uses `RustBuffer` as the FfiType - * - * @suppress - */ -public interface FfiConverterRustBuffer : FfiConverter { - override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) - - override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) -} -// A handful of classes and functions to support the generated data structures. -// This would be a good candidate for isolating in its own ffi-support lib. - -internal const val UNIFFI_CALL_SUCCESS = 0.toByte() -internal const val UNIFFI_CALL_ERROR = 1.toByte() -internal const val UNIFFI_CALL_UNEXPECTED_ERROR = 2.toByte() - -@Structure.FieldOrder("code", "error_buf") -internal open class UniffiRustCallStatus : Structure() { - @JvmField var code: Byte = 0 - - @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() - - class ByValue : - UniffiRustCallStatus(), - Structure.ByValue - - fun isSuccess(): Boolean = code == UNIFFI_CALL_SUCCESS - - fun isError(): Boolean = code == UNIFFI_CALL_ERROR - - fun isPanic(): Boolean = code == UNIFFI_CALL_UNEXPECTED_ERROR - - companion object { - fun create( - code: Byte, - errorBuf: RustBuffer.ByValue, - ): UniffiRustCallStatus.ByValue { - val callStatus = UniffiRustCallStatus.ByValue() - callStatus.code = code - callStatus.error_buf = errorBuf - return callStatus - } - } -} - -class InternalException( - message: String, -) : kotlin.Exception(message) - -/** - * Each top-level error class has a companion object that can lift the error from the call status's rust buffer - * - * @suppress - */ -interface UniffiRustCallStatusErrorHandler { - fun lift(error_buf: RustBuffer.ByValue): E -} - -// Helpers for calling Rust -// In practice we usually need to be synchronized to call this safely, so it doesn't -// synchronize itself - -// Call a rust function that returns a Result<>. Pass in the Error class companion that corresponds to the Err -private inline fun uniffiRustCallWithError( - errorHandler: UniffiRustCallStatusErrorHandler, - callback: (UniffiRustCallStatus) -> U, -): U { - var status = UniffiRustCallStatus() - val return_value = callback(status) - uniffiCheckCallStatus(errorHandler, status) - return return_value -} - -// Check UniffiRustCallStatus and throw an error if the call wasn't successful -private fun uniffiCheckCallStatus( - errorHandler: UniffiRustCallStatusErrorHandler, - status: UniffiRustCallStatus, -) { - if (status.isSuccess()) { - return - } else if (status.isError()) { - throw errorHandler.lift(status.error_buf) - } else if (status.isPanic()) { - // when the rust code sees a panic, it tries to construct a rustbuffer - // with the message. but if that code panics, then it just sends back - // an empty buffer. - if (status.error_buf.len > 0) { - throw InternalException(FfiConverterString.lift(status.error_buf)) - } else { - throw InternalException("Rust panic") - } - } else { - throw InternalException("Unknown rust call status: $status.code") - } -} - -/** - * UniffiRustCallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR - * - * @suppress - */ -object UniffiNullRustCallStatusErrorHandler : UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): InternalException { - RustBuffer.free(error_buf) - return InternalException("Unexpected CALL_ERROR") - } -} - -// Call a rust function that returns a plain value -private inline fun uniffiRustCall(callback: (UniffiRustCallStatus) -> U): U = - uniffiRustCallWithError(UniffiNullRustCallStatusErrorHandler, callback) - -internal inline fun uniffiTraitInterfaceCall( - callStatus: UniffiRustCallStatus, - makeCall: () -> T, - writeReturn: (T) -> Unit, -) { - try { - writeReturn(makeCall()) - } catch (e: kotlin.Exception) { - callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR - callStatus.error_buf = FfiConverterString.lower(e.toString()) - } -} - -internal inline fun uniffiTraitInterfaceCallWithError( - callStatus: UniffiRustCallStatus, - makeCall: () -> T, - writeReturn: (T) -> Unit, - lowerError: (E) -> RustBuffer.ByValue, -) { - try { - writeReturn(makeCall()) - } catch (e: kotlin.Exception) { - if (e is E) { - callStatus.code = UNIFFI_CALL_ERROR - callStatus.error_buf = lowerError(e) - } else { - callStatus.code = UNIFFI_CALL_UNEXPECTED_ERROR - callStatus.error_buf = FfiConverterString.lower(e.toString()) - } - } -} - -// Map handles to objects -// -// This is used pass an opaque 64-bit handle representing a foreign object to the Rust code. -internal class UniffiHandleMap { - private val map = ConcurrentHashMap() - private val counter = - java.util.concurrent.atomic - .AtomicLong(0) - - val size: Int - get() = map.size - - // Insert a new object into the handle map and get a handle for it - fun insert(obj: T): Long { - val handle = counter.getAndAdd(1) - map.put(handle, obj) - return handle - } - - // Get an object from the handle map - fun get(handle: Long): T = map.get(handle) ?: throw InternalException("UniffiHandleMap.get: Invalid handle") - - // Remove an entry from the handlemap and get the Kotlin object back - fun remove(handle: Long): T = map.remove(handle) ?: throw InternalException("UniffiHandleMap: Invalid handle") -} - -// Contains loading, initialization code, -// and the FFI Function declarations in a com.sun.jna.Library. -@Synchronized -private fun findLibraryName(componentName: String): String { - val libOverride = System.getProperty("uniffi.component.$componentName.libraryOverride") - if (libOverride != null) { - return libOverride - } - return "iroh_ffi" -} - -private inline fun loadIndirect(componentName: String): Lib = - Native.load(findLibraryName(componentName), Lib::class.java) - -// Define FFI callback types -internal interface UniffiRustFutureContinuationCallback : com.sun.jna.Callback { - fun callback( - `data`: Long, - `pollResult`: Byte, - ) -} - -internal interface UniffiForeignFutureFree : com.sun.jna.Callback { - fun callback(`handle`: Long) -} - -internal interface UniffiCallbackInterfaceFree : com.sun.jna.Callback { - fun callback(`handle`: Long) -} - -@Structure.FieldOrder("handle", "free") -internal open class UniffiForeignFuture( - @JvmField internal var `handle`: Long = 0.toLong(), - @JvmField internal var `free`: UniffiForeignFutureFree? = null, -) : Structure() { - class UniffiByValue( - `handle`: Long = 0.toLong(), - `free`: UniffiForeignFutureFree? = null, - ) : UniffiForeignFuture(`handle`, `free`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFuture) { - `handle` = other.`handle` - `free` = other.`free` - } -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU8( - @JvmField internal var `returnValue`: Byte = 0.toByte(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Byte = 0.toByte(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructU8(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU8) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteU8 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructU8.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI8( - @JvmField internal var `returnValue`: Byte = 0.toByte(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Byte = 0.toByte(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructI8(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI8) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteI8 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructI8.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU16( - @JvmField internal var `returnValue`: Short = 0.toShort(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Short = 0.toShort(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructU16(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU16) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteU16 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructU16.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI16( - @JvmField internal var `returnValue`: Short = 0.toShort(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Short = 0.toShort(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructI16(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI16) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteI16 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructI16.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU32( - @JvmField internal var `returnValue`: Int = 0, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Int = 0, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructU32(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU32) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteU32 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructU32.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI32( - @JvmField internal var `returnValue`: Int = 0, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Int = 0, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructI32(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI32) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteI32 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructI32.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructU64( - @JvmField internal var `returnValue`: Long = 0.toLong(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Long = 0.toLong(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructU64(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructU64) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteU64 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructU64.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructI64( - @JvmField internal var `returnValue`: Long = 0.toLong(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Long = 0.toLong(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructI64(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructI64) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteI64 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructI64.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructF32( - @JvmField internal var `returnValue`: Float = 0.0f, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Float = 0.0f, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructF32(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructF32) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteF32 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructF32.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructF64( - @JvmField internal var `returnValue`: Double = 0.0, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Double = 0.0, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructF64(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructF64) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteF64 : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructF64.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructPointer( - @JvmField internal var `returnValue`: Pointer = Pointer.NULL, - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: Pointer = Pointer.NULL, - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructPointer(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructPointer) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompletePointer : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructPointer.UniffiByValue, - ) -} - -@Structure.FieldOrder("returnValue", "callStatus") -internal open class UniffiForeignFutureStructRustBuffer( - @JvmField internal var `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `returnValue`: RustBuffer.ByValue = RustBuffer.ByValue(), - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructRustBuffer(`returnValue`, `callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructRustBuffer) { - `returnValue` = other.`returnValue` - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteRustBuffer : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructRustBuffer.UniffiByValue, - ) -} - -@Structure.FieldOrder("callStatus") -internal open class UniffiForeignFutureStructVoid( - @JvmField internal var `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), -) : Structure() { - class UniffiByValue( - `callStatus`: UniffiRustCallStatus.ByValue = UniffiRustCallStatus.ByValue(), - ) : UniffiForeignFutureStructVoid(`callStatus`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiForeignFutureStructVoid) { - `callStatus` = other.`callStatus` - } -} - -internal interface UniffiForeignFutureCompleteVoid : com.sun.jna.Callback { - fun callback( - `callbackData`: Long, - `result`: UniffiForeignFutureStructVoid.UniffiByValue, - ) -} - -internal interface UniffiCallbackInterfaceAddCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceBlobProvideEventCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `event`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceDocExportFileCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceDocImportFileCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceDownloadCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceGossipMessageCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `msg`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceProtocolCreatorMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `endpoint`: Pointer, - `uniffiOutReturn`: PointerByReference, - uniffiCallStatus: UniffiRustCallStatus, - ) -} - -internal interface UniffiCallbackInterfaceProtocolHandlerMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `conn`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceProtocolHandlerMethod1 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -internal interface UniffiCallbackInterfaceSubscribeCallbackMethod0 : com.sun.jna.Callback { - fun callback( - `uniffiHandle`: Long, - `event`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) -} - -@Structure.FieldOrder("progress", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceAddCallback( - @JvmField internal var `progress`: UniffiCallbackInterfaceAddCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `progress`: UniffiCallbackInterfaceAddCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceAddCallback(`progress`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceAddCallback) { - `progress` = other.`progress` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("blobEvent", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceBlobProvideEventCallback( - @JvmField internal var `blobEvent`: UniffiCallbackInterfaceBlobProvideEventCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `blobEvent`: UniffiCallbackInterfaceBlobProvideEventCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceBlobProvideEventCallback(`blobEvent`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceBlobProvideEventCallback) { - `blobEvent` = other.`blobEvent` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("progress", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceDocExportFileCallback( - @JvmField internal var `progress`: UniffiCallbackInterfaceDocExportFileCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `progress`: UniffiCallbackInterfaceDocExportFileCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceDocExportFileCallback(`progress`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceDocExportFileCallback) { - `progress` = other.`progress` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("progress", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceDocImportFileCallback( - @JvmField internal var `progress`: UniffiCallbackInterfaceDocImportFileCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `progress`: UniffiCallbackInterfaceDocImportFileCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceDocImportFileCallback(`progress`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceDocImportFileCallback) { - `progress` = other.`progress` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("progress", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceDownloadCallback( - @JvmField internal var `progress`: UniffiCallbackInterfaceDownloadCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `progress`: UniffiCallbackInterfaceDownloadCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceDownloadCallback(`progress`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceDownloadCallback) { - `progress` = other.`progress` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("onMessage", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceGossipMessageCallback( - @JvmField internal var `onMessage`: UniffiCallbackInterfaceGossipMessageCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `onMessage`: UniffiCallbackInterfaceGossipMessageCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceGossipMessageCallback(`onMessage`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceGossipMessageCallback) { - `onMessage` = other.`onMessage` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("create", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceProtocolCreator( - @JvmField internal var `create`: UniffiCallbackInterfaceProtocolCreatorMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `create`: UniffiCallbackInterfaceProtocolCreatorMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceProtocolCreator(`create`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceProtocolCreator) { - `create` = other.`create` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("accept", "shutdown", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceProtocolHandler( - @JvmField internal var `accept`: UniffiCallbackInterfaceProtocolHandlerMethod0? = null, - @JvmField internal var `shutdown`: UniffiCallbackInterfaceProtocolHandlerMethod1? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `accept`: UniffiCallbackInterfaceProtocolHandlerMethod0? = null, - `shutdown`: UniffiCallbackInterfaceProtocolHandlerMethod1? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceProtocolHandler(`accept`, `shutdown`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceProtocolHandler) { - `accept` = other.`accept` - `shutdown` = other.`shutdown` - `uniffiFree` = other.`uniffiFree` - } -} - -@Structure.FieldOrder("event", "uniffiFree") -internal open class UniffiVTableCallbackInterfaceSubscribeCallback( - @JvmField internal var `event`: UniffiCallbackInterfaceSubscribeCallbackMethod0? = null, - @JvmField internal var `uniffiFree`: UniffiCallbackInterfaceFree? = null, -) : Structure() { - class UniffiByValue( - `event`: UniffiCallbackInterfaceSubscribeCallbackMethod0? = null, - `uniffiFree`: UniffiCallbackInterfaceFree? = null, - ) : UniffiVTableCallbackInterfaceSubscribeCallback(`event`, `uniffiFree`), - Structure.ByValue - - internal fun uniffiSetValue(other: UniffiVTableCallbackInterfaceSubscribeCallback) { - `event` = other.`event` - `uniffiFree` = other.`uniffiFree` - } -} - -// A JNA Library to expose the extern-C FFI definitions. -// This is an implementation detail which will be called internally by the public API. - -internal interface UniffiLib : Library { - companion object { - internal val INSTANCE: UniffiLib by lazy { - loadIndirect(componentName = "iroh_ffi") - .also { lib: UniffiLib -> - uniffiCheckContractApiVersion(lib) - uniffiCheckApiChecksums(lib) - uniffiCallbackInterfaceAddCallback.register(lib) - uniffiCallbackInterfaceBlobProvideEventCallback.register(lib) - uniffiCallbackInterfaceDocExportFileCallback.register(lib) - uniffiCallbackInterfaceDocImportFileCallback.register(lib) - uniffiCallbackInterfaceDownloadCallback.register(lib) - uniffiCallbackInterfaceGossipMessageCallback.register(lib) - uniffiCallbackInterfaceProtocolCreator.register(lib) - uniffiCallbackInterfaceProtocolHandler.register(lib) - uniffiCallbackInterfaceSubscribeCallback.register(lib) - } - } - - // The Cleaner for the whole library - internal val CLEANER: UniffiCleaner by lazy { - UniffiCleaner.create() - } - } - - fun uniffi_iroh_ffi_fn_clone_addcallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_addcallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_addcallback(`vtable`: UniffiVTableCallbackInterfaceAddCallback): Unit - - fun uniffi_iroh_ffi_fn_method_addcallback_progress( - `ptr`: Pointer, - `progress`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_addprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_addprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_addprogress_as_abort( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_addprogress_as_all_done( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_addprogress_as_done( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_addprogress_as_found( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_addprogress_as_progress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_addprogress_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_author( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_author( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_author_from_string( - `str`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_author_id( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_author_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_authorid( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_authorid( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_authorid_from_string( - `str`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_authorid_equal( - `ptr`: Pointer, - `other`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_authorid_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_authors( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_authors( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_authors_create(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_authors_default(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_authors_delete( - `ptr`: Pointer, - `author`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_authors_export( - `ptr`: Pointer, - `author`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_authors_import( - `ptr`: Pointer, - `author`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_authors_import_author( - `ptr`: Pointer, - `author`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_authors_list(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_bistream( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_bistream( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_bistream_recv( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_bistream_send( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_blobdownloadoptions( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_blobdownloadoptions( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_blobdownloadoptions_new( - `format`: RustBuffer.ByValue, - `nodes`: RustBuffer.ByValue, - `tag`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_blobprovideevent( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_blobprovideevent( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_client_connected( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_get_request_received( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_tagged_blob_added( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_aborted( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_blob_completed( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_completed( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_hash_seq_started( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_progress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobprovideevent_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_blobprovideeventcallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_blobprovideeventcallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_blobprovideeventcallback( - `vtable`: UniffiVTableCallbackInterfaceBlobProvideEventCallback, - ): Unit - - fun uniffi_iroh_ffi_fn_method_blobprovideeventcallback_blob_event( - `ptr`: Pointer, - `event`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_blobstatus( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_blobstatus( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_clone_blobticket( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_blobticket( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_blobticket_new( - `str`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_blobticket_as_download_options( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_blobticket_format( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_blobticket_hash( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_blobticket_node_addr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_blobticket_recursive( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_blobticket_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_blobs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_blobs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_blobs_add_bytes( - `ptr`: Pointer, - `bytes`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_add_bytes_named( - `ptr`: Pointer, - `bytes`: RustBuffer.ByValue, - `name`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_add_from_path( - `ptr`: Pointer, - `path`: RustBuffer.ByValue, - `inPlace`: Byte, - `tag`: Pointer, - `wrap`: Pointer, - `cb`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_create_collection( - `ptr`: Pointer, - `collection`: Pointer, - `tag`: Pointer, - `tagsToDelete`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_delete_blob( - `ptr`: Pointer, - `hash`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_download( - `ptr`: Pointer, - `hash`: Pointer, - `opts`: Pointer, - `cb`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_export( - `ptr`: Pointer, - `hash`: Pointer, - `destination`: RustBuffer.ByValue, - `format`: RustBuffer.ByValue, - `mode`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_get_collection( - `ptr`: Pointer, - `hash`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_has( - `ptr`: Pointer, - `hash`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_list(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_blobs_list_collections(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_blobs_list_incomplete(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_blobs_read_at_to_bytes( - `ptr`: Pointer, - `hash`: Pointer, - `offset`: Long, - `len`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_read_to_bytes( - `ptr`: Pointer, - `hash`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_share( - `ptr`: Pointer, - `hash`: Pointer, - `blobFormat`: RustBuffer.ByValue, - `ticketOptions`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_size( - `ptr`: Pointer, - `hash`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_status( - `ptr`: Pointer, - `hash`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_blobs_write_to_path( - `ptr`: Pointer, - `hash`: Pointer, - `path`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_clone_collection( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_collection( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_collection_new(uniffi_out_err: UniffiRustCallStatus): Pointer - - fun uniffi_iroh_ffi_fn_method_collection_blobs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_collection_is_empty( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_collection_len( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_method_collection_links( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_collection_names( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_collection_push( - `ptr`: Pointer, - `name`: RustBuffer.ByValue, - `hash`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_clone_connecting( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_connecting( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connecting_alpn(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connecting_connect(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connecting_local_ip(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connecting_remote_address(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_connection( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_connection( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connection_accept_bi(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connection_accept_uni(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connection_close( - `ptr`: Pointer, - `errorCode`: Long, - `reason`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connection_close_reason( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connection_closed(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connection_datagram_send_buffer_space( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_method_connection_get_remote_node_id( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_connection_local_ip( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connection_max_datagram_size( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connection_open_bi(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connection_open_uni(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connection_read_datagram(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_connection_remote_address( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connection_rtt( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_method_connection_send_datagram( - `ptr`: Pointer, - `data`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connection_send_datagram_wait( - `ptr`: Pointer, - `data`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_connection_set_max_concurrent_bii_stream( - `ptr`: Pointer, - `count`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connection_set_max_concurrent_uni_stream( - `ptr`: Pointer, - `count`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connection_set_receive_window( - `ptr`: Pointer, - `count`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connection_stable_id( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_clone_connectiontype( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_connectiontype( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_connectiontype_as_direct( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connectiontype_as_mixed( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connectiontype_as_relay( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_connectiontype_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_directaddrinfo( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_directaddrinfo( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_directaddrinfo_addr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_directaddrinfo_last_control( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_directaddrinfo_last_payload( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_directaddrinfo_latency( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_doc( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_doc( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_doc_close_me(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_doc_delete( - `ptr`: Pointer, - `authorId`: Pointer, - `prefix`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_export_file( - `ptr`: Pointer, - `entry`: Pointer, - `path`: RustBuffer.ByValue, - `cb`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_get_download_policy(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_doc_get_exact( - `ptr`: Pointer, - `author`: Pointer, - `key`: RustBuffer.ByValue, - `includeEmpty`: Byte, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_get_many( - `ptr`: Pointer, - `query`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_get_one( - `ptr`: Pointer, - `query`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_get_sync_peers(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_doc_id( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_doc_import_file( - `ptr`: Pointer, - `author`: Pointer, - `key`: RustBuffer.ByValue, - `path`: RustBuffer.ByValue, - `inPlace`: Byte, - `cb`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_leave(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_doc_set_bytes( - `ptr`: Pointer, - `authorId`: Pointer, - `key`: RustBuffer.ByValue, - `value`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_set_download_policy( - `ptr`: Pointer, - `policy`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_set_hash( - `ptr`: Pointer, - `authorId`: Pointer, - `key`: RustBuffer.ByValue, - `hash`: Pointer, - `size`: Long, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_share( - `ptr`: Pointer, - `mode`: RustBuffer.ByValue, - `addrOptions`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_start_sync( - `ptr`: Pointer, - `peers`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_doc_status(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_doc_subscribe( - `ptr`: Pointer, - `cb`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_docexportfilecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_docexportfilecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_docexportfilecallback(`vtable`: UniffiVTableCallbackInterfaceDocExportFileCallback): Unit - - fun uniffi_iroh_ffi_fn_method_docexportfilecallback_progress( - `ptr`: Pointer, - `progress`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_docexportprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_docexportprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_docexportprogress_as_abort( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docexportprogress_as_found( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docexportprogress_as_progress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docexportprogress_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_docimportfilecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_docimportfilecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_docimportfilecallback(`vtable`: UniffiVTableCallbackInterfaceDocImportFileCallback): Unit - - fun uniffi_iroh_ffi_fn_method_docimportfilecallback_progress( - `ptr`: Pointer, - `progress`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_docimportprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_docimportprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_docimportprogress_as_abort( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docimportprogress_as_all_done( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docimportprogress_as_found( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docimportprogress_as_ingest_done( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docimportprogress_as_progress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_docimportprogress_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_docticket( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_docticket( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_docticket_new( - `str`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_docticket_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_docs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_docs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_docs_create(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_docs_drop_doc( - `ptr`: Pointer, - `docId`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_docs_join( - `ptr`: Pointer, - `ticket`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_docs_join_and_subscribe( - `ptr`: Pointer, - `ticket`: Pointer, - `cb`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_docs_list(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_docs_open( - `ptr`: Pointer, - `id`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_clone_downloadcallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_downloadcallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_downloadcallback(`vtable`: UniffiVTableCallbackInterfaceDownloadCallback): Unit - - fun uniffi_iroh_ffi_fn_method_downloadcallback_progress( - `ptr`: Pointer, - `progress`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_downloadpolicy( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_downloadpolicy( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_downloadpolicy_everything(uniffi_out_err: UniffiRustCallStatus): Pointer - - fun uniffi_iroh_ffi_fn_constructor_downloadpolicy_everything_except( - `filters`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_downloadpolicy_nothing(uniffi_out_err: UniffiRustCallStatus): Pointer - - fun uniffi_iroh_ffi_fn_constructor_downloadpolicy_nothing_except( - `filters`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_downloadprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_downloadprogress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_abort( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_all_done( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_done( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_found( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_found_hash_seq( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_found_local( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_as_progress( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_downloadprogress_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_endpoint( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_endpoint( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_endpoint_connect( - `ptr`: Pointer, - `nodeAddr`: Pointer, - `alpn`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_endpoint_node_id( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_entry( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_entry( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_entry_author( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_entry_content_hash( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_entry_content_len( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_method_entry_key( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_entry_namespace( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_entry_timestamp( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_clone_filterkind( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_filterkind( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_filterkind_exact( - `key`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_filterkind_prefix( - `prefix`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_filterkind_matches( - `ptr`: Pointer, - `key`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_clone_gossip( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_gossip( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_gossip_subscribe( - `ptr`: Pointer, - `topic`: RustBuffer.ByValue, - `bootstrap`: RustBuffer.ByValue, - `cb`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_gossipmessagecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_gossipmessagecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_gossipmessagecallback(`vtable`: UniffiVTableCallbackInterfaceGossipMessageCallback): Unit - - fun uniffi_iroh_ffi_fn_method_gossipmessagecallback_on_message( - `ptr`: Pointer, - `msg`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_hash( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_hash( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_hash_from_bytes( - `bytes`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_hash_from_string( - `s`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_hash_new( - `buf`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_hash_equal( - `ptr`: Pointer, - `other`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_hash_to_bytes( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_hash_to_hex( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_hash_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_iroh( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_iroh( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_iroh_memory(): Long - - fun uniffi_iroh_ffi_fn_constructor_iroh_memory_with_options(`options`: RustBuffer.ByValue): Long - - fun uniffi_iroh_ffi_fn_constructor_iroh_persistent(`path`: RustBuffer.ByValue): Long - - fun uniffi_iroh_ffi_fn_constructor_iroh_persistent_with_options( - `path`: RustBuffer.ByValue, - `options`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_iroh_authors( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_iroh_blobs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_iroh_docs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_iroh_gossip( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_iroh_net( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_iroh_node( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_iroh_tags( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_iroherror( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_iroherror( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_iroherror_message( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_iroherror_uniffi_trait_debug( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_liveevent( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_liveevent( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_liveevent_as_content_ready( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_liveevent_as_insert_local( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_liveevent_as_insert_remote( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_liveevent_as_neighbor_down( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_liveevent_as_neighbor_up( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_liveevent_as_sync_finished( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_liveevent_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_message( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_message( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_message_as_error( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_message_as_joined( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_message_as_neighbor_down( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_message_as_neighbor_up( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_message_as_received( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_message_type( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_net( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_net( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_net_add_node_addr( - `ptr`: Pointer, - `addr`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_net_home_relay(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_net_node_addr(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_net_node_id(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_net_remote_info( - `ptr`: Pointer, - `nodeId`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_net_remote_info_list(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_node( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_node( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_node_endpoint( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_node_shutdown(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_node_stats(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_node_status(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_nodeaddr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_nodeaddr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_nodeaddr_new( - `nodeId`: Pointer, - `derpUrl`: RustBuffer.ByValue, - `addresses`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_nodeaddr_direct_addresses( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_nodeaddr_equal( - `ptr`: Pointer, - `other`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_nodeaddr_relay_url( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_nodestatus( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_nodestatus( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_nodestatus_listen_addrs( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_nodestatus_node_addr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_nodestatus_rpc_addr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_nodestatus_version( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_nodeticket( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_nodeticket( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_nodeticket_new( - `addr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_nodeticket_parse( - `str`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_nodeticket_node_addr( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_nodeticket_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_protocolcreator( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_protocolcreator( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_protocolcreator(`vtable`: UniffiVTableCallbackInterfaceProtocolCreator): Unit - - fun uniffi_iroh_ffi_fn_method_protocolcreator_create( - `ptr`: Pointer, - `endpoint`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_protocolhandler( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_protocolhandler( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_protocolhandler(`vtable`: UniffiVTableCallbackInterfaceProtocolHandler): Unit - - fun uniffi_iroh_ffi_fn_method_protocolhandler_accept( - `ptr`: Pointer, - `conn`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_method_protocolhandler_shutdown(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_publickey( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_publickey( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_publickey_from_bytes( - `bytes`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_publickey_from_string( - `s`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_publickey_equal( - `ptr`: Pointer, - `other`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_publickey_fmt_short( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_publickey_to_bytes( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_publickey_uniffi_trait_display( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_clone_query( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_query( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_query_all( - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_author( - `author`: Pointer, - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_author_key_exact( - `author`: Pointer, - `key`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_author_key_prefix( - `author`: Pointer, - `prefix`: RustBuffer.ByValue, - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_key_exact( - `key`: RustBuffer.ByValue, - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_key_prefix( - `prefix`: RustBuffer.ByValue, - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_single_latest_per_key( - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_single_latest_per_key_exact( - `key`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_query_single_latest_per_key_prefix( - `prefix`: RustBuffer.ByValue, - `opts`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_method_query_limit( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_method_query_offset( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun uniffi_iroh_ffi_fn_clone_rangespec( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_rangespec( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_rangespec_is_all( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_method_rangespec_is_empty( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun uniffi_iroh_ffi_fn_clone_readatlen( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_readatlen( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_readatlen_all(uniffi_out_err: UniffiRustCallStatus): Pointer - - fun uniffi_iroh_ffi_fn_constructor_readatlen_at_most( - `size`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_constructor_readatlen_exact( - `size`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_recvstream( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_recvstream( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_recvstream_id(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_recvstream_read( - `ptr`: Pointer, - `sizeLimit`: Int, - ): Long - - fun uniffi_iroh_ffi_fn_method_recvstream_read_exact( - `ptr`: Pointer, - `size`: Int, - ): Long - - fun uniffi_iroh_ffi_fn_method_recvstream_read_to_end( - `ptr`: Pointer, - `sizeLimit`: Int, - ): Long - - fun uniffi_iroh_ffi_fn_method_recvstream_received_reset(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_recvstream_stop( - `ptr`: Pointer, - `errorCode`: Long, - ): Long - - fun uniffi_iroh_ffi_fn_clone_sendstream( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_sendstream( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_sendstream_finish(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_id(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_priority(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_reset( - `ptr`: Pointer, - `errorCode`: Long, - ): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_set_priority( - `ptr`: Pointer, - `p`: Int, - ): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_stopped(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_write( - `ptr`: Pointer, - `buf`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_sendstream_write_all( - `ptr`: Pointer, - `buf`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_clone_sender( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_sender( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_sender_broadcast( - `ptr`: Pointer, - `msg`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_sender_broadcast_neighbors( - `ptr`: Pointer, - `msg`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_sender_cancel(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_settagoption( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_settagoption( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_settagoption_auto(uniffi_out_err: UniffiRustCallStatus): Pointer - - fun uniffi_iroh_ffi_fn_constructor_settagoption_named( - `tag`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_clone_subscribecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_subscribecallback( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_init_callback_vtable_subscribecallback(`vtable`: UniffiVTableCallbackInterfaceSubscribeCallback): Unit - - fun uniffi_iroh_ffi_fn_method_subscribecallback_event( - `ptr`: Pointer, - `event`: Pointer, - ): Long - - fun uniffi_iroh_ffi_fn_clone_tags( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_tags( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_method_tags_delete( - `ptr`: Pointer, - `name`: RustBuffer.ByValue, - ): Long - - fun uniffi_iroh_ffi_fn_method_tags_list(`ptr`: Pointer): Long - - fun uniffi_iroh_ffi_fn_clone_wrapoption( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_free_wrapoption( - `ptr`: Pointer, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_constructor_wrapoption_no_wrap(uniffi_out_err: UniffiRustCallStatus): Pointer - - fun uniffi_iroh_ffi_fn_constructor_wrapoption_wrap( - `name`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun uniffi_iroh_ffi_fn_func_key_to_path( - `key`: RustBuffer.ByValue, - `prefix`: RustBuffer.ByValue, - `root`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_func_path_to_key( - `path`: RustBuffer.ByValue, - `prefix`: RustBuffer.ByValue, - `root`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun uniffi_iroh_ffi_fn_func_set_log_level( - `level`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_fn_func_start_metrics_collection(uniffi_out_err: UniffiRustCallStatus): Unit - - fun ffi_iroh_ffi_rustbuffer_alloc( - `size`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun ffi_iroh_ffi_rustbuffer_from_bytes( - `bytes`: ForeignBytes.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun ffi_iroh_ffi_rustbuffer_free( - `buf`: RustBuffer.ByValue, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun ffi_iroh_ffi_rustbuffer_reserve( - `buf`: RustBuffer.ByValue, - `additional`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun ffi_iroh_ffi_rust_future_poll_u8( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_u8(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_u8(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_u8( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun ffi_iroh_ffi_rust_future_poll_i8( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_i8(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_i8(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_i8( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Byte - - fun ffi_iroh_ffi_rust_future_poll_u16( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_u16(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_u16(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_u16( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Short - - fun ffi_iroh_ffi_rust_future_poll_i16( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_i16(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_i16(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_i16( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Short - - fun ffi_iroh_ffi_rust_future_poll_u32( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_u32(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_u32(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_u32( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Int - - fun ffi_iroh_ffi_rust_future_poll_i32( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_i32(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_i32(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_i32( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Int - - fun ffi_iroh_ffi_rust_future_poll_u64( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_u64(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_u64(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_u64( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun ffi_iroh_ffi_rust_future_poll_i64( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_i64(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_i64(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_i64( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Long - - fun ffi_iroh_ffi_rust_future_poll_f32( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_f32(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_f32(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_f32( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Float - - fun ffi_iroh_ffi_rust_future_poll_f64( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_f64(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_f64(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_f64( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Double - - fun ffi_iroh_ffi_rust_future_poll_pointer( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_pointer(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_pointer(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_pointer( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Pointer - - fun ffi_iroh_ffi_rust_future_poll_rust_buffer( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_rust_buffer(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_rust_buffer(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_rust_buffer( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): RustBuffer.ByValue - - fun ffi_iroh_ffi_rust_future_poll_void( - `handle`: Long, - `callback`: UniffiRustFutureContinuationCallback, - `callbackData`: Long, - ): Unit - - fun ffi_iroh_ffi_rust_future_cancel_void(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_free_void(`handle`: Long): Unit - - fun ffi_iroh_ffi_rust_future_complete_void( - `handle`: Long, - uniffi_out_err: UniffiRustCallStatus, - ): Unit - - fun uniffi_iroh_ffi_checksum_func_key_to_path(): Short - - fun uniffi_iroh_ffi_checksum_func_path_to_key(): Short - - fun uniffi_iroh_ffi_checksum_func_set_log_level(): Short - - fun uniffi_iroh_ffi_checksum_func_start_metrics_collection(): Short - - fun uniffi_iroh_ffi_checksum_method_addcallback_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_addprogress_as_abort(): Short - - fun uniffi_iroh_ffi_checksum_method_addprogress_as_all_done(): Short - - fun uniffi_iroh_ffi_checksum_method_addprogress_as_done(): Short - - fun uniffi_iroh_ffi_checksum_method_addprogress_as_found(): Short - - fun uniffi_iroh_ffi_checksum_method_addprogress_as_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_addprogress_type(): Short - - fun uniffi_iroh_ffi_checksum_method_author_id(): Short - - fun uniffi_iroh_ffi_checksum_method_authorid_equal(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_create(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_default(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_delete(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_export(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_import(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_import_author(): Short - - fun uniffi_iroh_ffi_checksum_method_authors_list(): Short - - fun uniffi_iroh_ffi_checksum_method_bistream_recv(): Short - - fun uniffi_iroh_ffi_checksum_method_bistream_send(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_client_connected(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_get_request_received(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_tagged_blob_added(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_aborted(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_blob_completed(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_completed(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_hash_seq_started(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideevent_type(): Short - - fun uniffi_iroh_ffi_checksum_method_blobprovideeventcallback_blob_event(): Short - - fun uniffi_iroh_ffi_checksum_method_blobticket_as_download_options(): Short - - fun uniffi_iroh_ffi_checksum_method_blobticket_format(): Short - - fun uniffi_iroh_ffi_checksum_method_blobticket_hash(): Short - - fun uniffi_iroh_ffi_checksum_method_blobticket_node_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_blobticket_recursive(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_add_bytes(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_add_bytes_named(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_add_from_path(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_create_collection(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_delete_blob(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_download(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_export(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_get_collection(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_has(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_list(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_list_collections(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_list_incomplete(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_read_at_to_bytes(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_read_to_bytes(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_share(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_size(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_status(): Short - - fun uniffi_iroh_ffi_checksum_method_blobs_write_to_path(): Short - - fun uniffi_iroh_ffi_checksum_method_collection_blobs(): Short - - fun uniffi_iroh_ffi_checksum_method_collection_is_empty(): Short - - fun uniffi_iroh_ffi_checksum_method_collection_len(): Short - - fun uniffi_iroh_ffi_checksum_method_collection_links(): Short - - fun uniffi_iroh_ffi_checksum_method_collection_names(): Short - - fun uniffi_iroh_ffi_checksum_method_collection_push(): Short - - fun uniffi_iroh_ffi_checksum_method_connecting_alpn(): Short - - fun uniffi_iroh_ffi_checksum_method_connecting_connect(): Short - - fun uniffi_iroh_ffi_checksum_method_connecting_local_ip(): Short - - fun uniffi_iroh_ffi_checksum_method_connecting_remote_address(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_accept_bi(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_accept_uni(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_close(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_close_reason(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_closed(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_datagram_send_buffer_space(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_get_remote_node_id(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_local_ip(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_max_datagram_size(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_open_bi(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_open_uni(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_read_datagram(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_remote_address(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_rtt(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_send_datagram(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_send_datagram_wait(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_set_max_concurrent_bii_stream(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_set_max_concurrent_uni_stream(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_set_receive_window(): Short - - fun uniffi_iroh_ffi_checksum_method_connection_stable_id(): Short - - fun uniffi_iroh_ffi_checksum_method_connectiontype_as_direct(): Short - - fun uniffi_iroh_ffi_checksum_method_connectiontype_as_mixed(): Short - - fun uniffi_iroh_ffi_checksum_method_connectiontype_as_relay(): Short - - fun uniffi_iroh_ffi_checksum_method_connectiontype_type(): Short - - fun uniffi_iroh_ffi_checksum_method_directaddrinfo_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_directaddrinfo_last_control(): Short - - fun uniffi_iroh_ffi_checksum_method_directaddrinfo_last_payload(): Short - - fun uniffi_iroh_ffi_checksum_method_directaddrinfo_latency(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_close_me(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_delete(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_export_file(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_get_download_policy(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_get_exact(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_get_many(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_get_one(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_get_sync_peers(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_id(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_import_file(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_leave(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_set_bytes(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_set_download_policy(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_set_hash(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_share(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_start_sync(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_status(): Short - - fun uniffi_iroh_ffi_checksum_method_doc_subscribe(): Short - - fun uniffi_iroh_ffi_checksum_method_docexportfilecallback_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_docexportprogress_as_abort(): Short - - fun uniffi_iroh_ffi_checksum_method_docexportprogress_as_found(): Short - - fun uniffi_iroh_ffi_checksum_method_docexportprogress_as_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_docexportprogress_type(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportfilecallback_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportprogress_as_abort(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportprogress_as_all_done(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportprogress_as_found(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportprogress_as_ingest_done(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportprogress_as_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_docimportprogress_type(): Short - - fun uniffi_iroh_ffi_checksum_method_docs_create(): Short - - fun uniffi_iroh_ffi_checksum_method_docs_drop_doc(): Short - - fun uniffi_iroh_ffi_checksum_method_docs_join(): Short - - fun uniffi_iroh_ffi_checksum_method_docs_join_and_subscribe(): Short - - fun uniffi_iroh_ffi_checksum_method_docs_list(): Short - - fun uniffi_iroh_ffi_checksum_method_docs_open(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadcallback_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_abort(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_all_done(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_done(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_found(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_found_hash_seq(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_found_local(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_as_progress(): Short - - fun uniffi_iroh_ffi_checksum_method_downloadprogress_type(): Short - - fun uniffi_iroh_ffi_checksum_method_endpoint_connect(): Short - - fun uniffi_iroh_ffi_checksum_method_endpoint_node_id(): Short - - fun uniffi_iroh_ffi_checksum_method_entry_author(): Short - - fun uniffi_iroh_ffi_checksum_method_entry_content_hash(): Short - - fun uniffi_iroh_ffi_checksum_method_entry_content_len(): Short - - fun uniffi_iroh_ffi_checksum_method_entry_key(): Short - - fun uniffi_iroh_ffi_checksum_method_entry_namespace(): Short - - fun uniffi_iroh_ffi_checksum_method_entry_timestamp(): Short - - fun uniffi_iroh_ffi_checksum_method_filterkind_matches(): Short - - fun uniffi_iroh_ffi_checksum_method_gossip_subscribe(): Short - - fun uniffi_iroh_ffi_checksum_method_gossipmessagecallback_on_message(): Short - - fun uniffi_iroh_ffi_checksum_method_hash_equal(): Short - - fun uniffi_iroh_ffi_checksum_method_hash_to_bytes(): Short - - fun uniffi_iroh_ffi_checksum_method_hash_to_hex(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_authors(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_blobs(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_docs(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_gossip(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_net(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_node(): Short - - fun uniffi_iroh_ffi_checksum_method_iroh_tags(): Short - - fun uniffi_iroh_ffi_checksum_method_iroherror_message(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_as_content_ready(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_as_insert_local(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_as_insert_remote(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_as_neighbor_down(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_as_neighbor_up(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_as_sync_finished(): Short - - fun uniffi_iroh_ffi_checksum_method_liveevent_type(): Short - - fun uniffi_iroh_ffi_checksum_method_message_as_error(): Short - - fun uniffi_iroh_ffi_checksum_method_message_as_joined(): Short - - fun uniffi_iroh_ffi_checksum_method_message_as_neighbor_down(): Short - - fun uniffi_iroh_ffi_checksum_method_message_as_neighbor_up(): Short - - fun uniffi_iroh_ffi_checksum_method_message_as_received(): Short - - fun uniffi_iroh_ffi_checksum_method_message_type(): Short - - fun uniffi_iroh_ffi_checksum_method_net_add_node_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_net_home_relay(): Short - - fun uniffi_iroh_ffi_checksum_method_net_node_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_net_node_id(): Short - - fun uniffi_iroh_ffi_checksum_method_net_remote_info(): Short - - fun uniffi_iroh_ffi_checksum_method_net_remote_info_list(): Short - - fun uniffi_iroh_ffi_checksum_method_node_endpoint(): Short - - fun uniffi_iroh_ffi_checksum_method_node_shutdown(): Short - - fun uniffi_iroh_ffi_checksum_method_node_stats(): Short - - fun uniffi_iroh_ffi_checksum_method_node_status(): Short - - fun uniffi_iroh_ffi_checksum_method_nodeaddr_direct_addresses(): Short - - fun uniffi_iroh_ffi_checksum_method_nodeaddr_equal(): Short - - fun uniffi_iroh_ffi_checksum_method_nodeaddr_relay_url(): Short - - fun uniffi_iroh_ffi_checksum_method_nodestatus_listen_addrs(): Short - - fun uniffi_iroh_ffi_checksum_method_nodestatus_node_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_nodestatus_rpc_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_nodestatus_version(): Short - - fun uniffi_iroh_ffi_checksum_method_nodeticket_node_addr(): Short - - fun uniffi_iroh_ffi_checksum_method_protocolcreator_create(): Short - - fun uniffi_iroh_ffi_checksum_method_protocolhandler_accept(): Short - - fun uniffi_iroh_ffi_checksum_method_protocolhandler_shutdown(): Short - - fun uniffi_iroh_ffi_checksum_method_publickey_equal(): Short - - fun uniffi_iroh_ffi_checksum_method_publickey_fmt_short(): Short - - fun uniffi_iroh_ffi_checksum_method_publickey_to_bytes(): Short - - fun uniffi_iroh_ffi_checksum_method_query_limit(): Short - - fun uniffi_iroh_ffi_checksum_method_query_offset(): Short - - fun uniffi_iroh_ffi_checksum_method_rangespec_is_all(): Short - - fun uniffi_iroh_ffi_checksum_method_rangespec_is_empty(): Short - - fun uniffi_iroh_ffi_checksum_method_recvstream_id(): Short - - fun uniffi_iroh_ffi_checksum_method_recvstream_read(): Short - - fun uniffi_iroh_ffi_checksum_method_recvstream_read_exact(): Short - - fun uniffi_iroh_ffi_checksum_method_recvstream_read_to_end(): Short - - fun uniffi_iroh_ffi_checksum_method_recvstream_received_reset(): Short - - fun uniffi_iroh_ffi_checksum_method_recvstream_stop(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_finish(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_id(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_priority(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_reset(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_set_priority(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_stopped(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_write(): Short - - fun uniffi_iroh_ffi_checksum_method_sendstream_write_all(): Short - - fun uniffi_iroh_ffi_checksum_method_sender_broadcast(): Short - - fun uniffi_iroh_ffi_checksum_method_sender_broadcast_neighbors(): Short - - fun uniffi_iroh_ffi_checksum_method_sender_cancel(): Short - - fun uniffi_iroh_ffi_checksum_method_subscribecallback_event(): Short - - fun uniffi_iroh_ffi_checksum_method_tags_delete(): Short - - fun uniffi_iroh_ffi_checksum_method_tags_list(): Short - - fun uniffi_iroh_ffi_checksum_constructor_author_from_string(): Short - - fun uniffi_iroh_ffi_checksum_constructor_authorid_from_string(): Short - - fun uniffi_iroh_ffi_checksum_constructor_blobdownloadoptions_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_blobticket_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_collection_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_docticket_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_downloadpolicy_everything(): Short - - fun uniffi_iroh_ffi_checksum_constructor_downloadpolicy_everything_except(): Short - - fun uniffi_iroh_ffi_checksum_constructor_downloadpolicy_nothing(): Short - - fun uniffi_iroh_ffi_checksum_constructor_downloadpolicy_nothing_except(): Short - - fun uniffi_iroh_ffi_checksum_constructor_filterkind_exact(): Short - - fun uniffi_iroh_ffi_checksum_constructor_filterkind_prefix(): Short - - fun uniffi_iroh_ffi_checksum_constructor_hash_from_bytes(): Short - - fun uniffi_iroh_ffi_checksum_constructor_hash_from_string(): Short - - fun uniffi_iroh_ffi_checksum_constructor_hash_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_iroh_memory(): Short - - fun uniffi_iroh_ffi_checksum_constructor_iroh_memory_with_options(): Short - - fun uniffi_iroh_ffi_checksum_constructor_iroh_persistent(): Short - - fun uniffi_iroh_ffi_checksum_constructor_iroh_persistent_with_options(): Short - - fun uniffi_iroh_ffi_checksum_constructor_nodeaddr_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_nodeticket_new(): Short - - fun uniffi_iroh_ffi_checksum_constructor_nodeticket_parse(): Short - - fun uniffi_iroh_ffi_checksum_constructor_publickey_from_bytes(): Short - - fun uniffi_iroh_ffi_checksum_constructor_publickey_from_string(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_all(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_author(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_author_key_exact(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_author_key_prefix(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_key_exact(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_key_prefix(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_single_latest_per_key(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_single_latest_per_key_exact(): Short - - fun uniffi_iroh_ffi_checksum_constructor_query_single_latest_per_key_prefix(): Short - - fun uniffi_iroh_ffi_checksum_constructor_readatlen_all(): Short - - fun uniffi_iroh_ffi_checksum_constructor_readatlen_at_most(): Short - - fun uniffi_iroh_ffi_checksum_constructor_readatlen_exact(): Short - - fun uniffi_iroh_ffi_checksum_constructor_settagoption_auto(): Short - - fun uniffi_iroh_ffi_checksum_constructor_settagoption_named(): Short - - fun uniffi_iroh_ffi_checksum_constructor_wrapoption_no_wrap(): Short - - fun uniffi_iroh_ffi_checksum_constructor_wrapoption_wrap(): Short - - fun ffi_iroh_ffi_uniffi_contract_version(): Int -} - -private fun uniffiCheckContractApiVersion(lib: UniffiLib) { - // Get the bindings contract version from our ComponentInterface - val bindings_contract_version = 26 - // Get the scaffolding contract version by calling the into the dylib - val scaffolding_contract_version = lib.ffi_iroh_ffi_uniffi_contract_version() - if (bindings_contract_version != scaffolding_contract_version) { - throw RuntimeException("UniFFI contract version mismatch: try cleaning and rebuilding your project") - } -} - -@Suppress("UNUSED_PARAMETER") -private fun uniffiCheckApiChecksums(lib: UniffiLib) { - if (lib.uniffi_iroh_ffi_checksum_func_key_to_path() != 28001.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_func_path_to_key() != 4438.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_func_set_log_level() != 52619.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_func_start_metrics_collection() != 23413.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addcallback_progress() != 62116.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addprogress_as_abort() != 44667.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addprogress_as_all_done() != 62551.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addprogress_as_done() != 58505.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addprogress_as_found() != 8172.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addprogress_as_progress() != 36155.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_addprogress_type() != 46221.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_author_id() != 39022.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authorid_equal() != 56356.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_create() != 47692.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_default() != 6795.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_delete() != 51040.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_export() != 17391.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_import() != 11067.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_import_author() != 56460.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_authors_list() != 33930.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_bistream_recv() != 60625.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_bistream_send() != 13146.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_client_connected() != 48446.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_get_request_received() != 8740.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_tagged_blob_added() != 59887.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_aborted() != 41238.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_blob_completed() != 20663.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_completed() != 47368.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_hash_seq_started() != 27778.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_as_transfer_progress() != 40626.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideevent_type() != 51159.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobprovideeventcallback_blob_event() != 43399.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobticket_as_download_options() != 18713.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobticket_format() != 35808.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobticket_hash() != 54061.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobticket_node_addr() != 30662.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobticket_recursive() != 53797.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_add_bytes() != 16525.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_add_bytes_named() != 4623.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_add_from_path() != 12412.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_create_collection() != 63440.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_delete_blob() != 24901.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_download() != 14779.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_export() != 23697.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_get_collection() != 57130.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_has() != 1301.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_list() != 9714.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_list_collections() != 22274.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_list_incomplete() != 31740.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_read_at_to_bytes() != 43209.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_read_to_bytes() != 13624.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_share() != 35831.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_size() != 20254.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_status() != 34093.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_blobs_write_to_path() != 47517.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_collection_blobs() != 52509.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_collection_is_empty() != 40621.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_collection_len() != 10206.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_collection_links() != 56034.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_collection_names() != 28871.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_collection_push() != 22031.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connecting_alpn() != 45347.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connecting_connect() != 64341.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connecting_local_ip() != 3368.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connecting_remote_address() != 25819.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_accept_bi() != 10996.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_accept_uni() != 17891.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_close() != 61009.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_close_reason() != 44737.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_closed() != 30404.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_datagram_send_buffer_space() != 52904.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_get_remote_node_id() != 64024.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_local_ip() != 11203.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_max_datagram_size() != 49257.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_open_bi() != 34801.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_open_uni() != 36079.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_read_datagram() != 23201.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_remote_address() != 60000.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_rtt() != 61654.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_send_datagram() != 105.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_send_datagram_wait() != 3162.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_set_max_concurrent_bii_stream() != 13576.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_set_max_concurrent_uni_stream() != 26642.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_set_receive_window() != 27731.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connection_stable_id() != 28186.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connectiontype_as_direct() != 47530.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connectiontype_as_mixed() != 49068.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connectiontype_as_relay() != 6121.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_connectiontype_type() != 54998.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_directaddrinfo_addr() != 20100.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_directaddrinfo_last_control() != 35048.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_directaddrinfo_last_payload() != 12406.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_directaddrinfo_latency() != 7414.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_close_me() != 13449.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_delete() != 54552.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_export_file() != 16067.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_get_download_policy() != 44884.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_get_exact() != 20423.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_get_many() != 53909.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_get_one() != 18797.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_get_sync_peers() != 59505.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_id() != 53450.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_import_file() != 52327.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_leave() != 40204.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_set_bytes() != 32483.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_set_download_policy() != 18200.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_set_hash() != 30875.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_share() != 59706.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_start_sync() != 54450.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_status() != 30558.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_doc_subscribe() != 59807.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docexportfilecallback_progress() != 53186.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docexportprogress_as_abort() != 34476.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docexportprogress_as_found() != 23982.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docexportprogress_as_progress() != 44802.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docexportprogress_type() != 11215.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportfilecallback_progress() != 55347.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportprogress_as_abort() != 35952.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportprogress_as_all_done() != 35787.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportprogress_as_found() != 6030.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportprogress_as_ingest_done() != 36.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportprogress_as_progress() != 19927.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docimportprogress_type() != 48401.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docs_create() != 54486.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docs_drop_doc() != 5864.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docs_join() != 38489.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docs_join_and_subscribe() != 41379.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docs_list() != 23866.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_docs_open() != 45928.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadcallback_progress() != 21881.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_abort() != 6879.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_all_done() != 4219.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_done() != 21859.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_found() != 47836.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_found_hash_seq() != 14451.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_found_local() != 47262.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_as_progress() != 16155.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_downloadprogress_type() != 60534.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_endpoint_connect() != 29734.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_endpoint_node_id() != 54517.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_entry_author() != 39787.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_entry_content_hash() != 26949.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_entry_content_len() != 40073.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_entry_key() != 10200.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_entry_namespace() != 25213.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_entry_timestamp() != 38377.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_filterkind_matches() != 24522.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_gossip_subscribe() != 6414.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_gossipmessagecallback_on_message() != 49150.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_hash_equal() != 28210.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_hash_to_bytes() != 26394.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_hash_to_hex() != 52108.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_authors() != 61389.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_blobs() != 50340.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_docs() != 17607.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_gossip() != 58884.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_net() != 41953.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_node() != 12499.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroh_tags() != 59606.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_iroherror_message() != 31085.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_as_content_ready() != 6578.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_as_insert_local() != 27496.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_as_insert_remote() != 38454.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_as_neighbor_down() != 27752.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_as_neighbor_up() != 44203.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_as_sync_finished() != 27893.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_liveevent_type() != 30099.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_message_as_error() != 9059.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_message_as_joined() != 39463.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_message_as_neighbor_down() != 19092.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_message_as_neighbor_up() != 3541.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_message_as_received() != 6044.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_message_type() != 75.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_net_add_node_addr() != 17723.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_net_home_relay() != 3492.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_net_node_addr() != 60712.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_net_node_id() != 35201.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_net_remote_info() != 60537.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_net_remote_info_list() != 15919.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_node_endpoint() != 6829.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_node_shutdown() != 49624.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_node_stats() != 13439.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_node_status() != 21889.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodeaddr_direct_addresses() != 23787.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodeaddr_equal() != 19664.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodeaddr_relay_url() != 34772.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodestatus_listen_addrs() != 54436.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodestatus_node_addr() != 12507.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodestatus_rpc_addr() != 20002.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodestatus_version() != 3183.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_nodeticket_node_addr() != 3397.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_protocolcreator_create() != 33391.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_protocolhandler_accept() != 54515.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_protocolhandler_shutdown() != 55574.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_publickey_equal() != 8690.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_publickey_fmt_short() != 31871.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_publickey_to_bytes() != 22449.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_query_limit() != 23235.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_query_offset() != 14460.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_rangespec_is_all() != 51737.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_rangespec_is_empty() != 38175.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_recvstream_id() != 17291.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_recvstream_read() != 25331.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_recvstream_read_exact() != 37269.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_recvstream_read_to_end() != 31754.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_recvstream_received_reset() != 12049.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_recvstream_stop() != 5360.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_finish() != 32400.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_id() != 905.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_priority() != 33897.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_reset() != 34438.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_set_priority() != 1968.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_stopped() != 40814.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_write() != 61923.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sendstream_write_all() != 5755.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sender_broadcast() != 42694.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sender_broadcast_neighbors() != 14000.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_sender_cancel() != 24357.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_subscribecallback_event() != 35520.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_tags_delete() != 17755.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_method_tags_list() != 16151.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_author_from_string() != 63158.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_authorid_from_string() != 47849.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_blobdownloadoptions_new() != 46030.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_blobticket_new() != 29763.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_collection_new() != 3798.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_docticket_new() != 29537.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_downloadpolicy_everything() != 35143.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_downloadpolicy_everything_except() != 21211.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_downloadpolicy_nothing() != 16928.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_downloadpolicy_nothing_except() != 12041.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_filterkind_exact() != 13432.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_filterkind_prefix() != 42338.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_hash_from_bytes() != 13104.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_hash_from_string() != 23453.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_hash_new() != 30613.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_iroh_memory() != 49939.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_iroh_memory_with_options() != 60437.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_iroh_persistent() != 42623.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_iroh_persistent_with_options() != 60788.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_nodeaddr_new() != 5759.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_nodeticket_new() != 8609.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_nodeticket_parse() != 16834.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_publickey_from_bytes() != 64011.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_publickey_from_string() != 42207.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_all() != 34328.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_author() != 17803.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_author_key_exact() != 38571.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_author_key_prefix() != 48731.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_key_exact() != 17481.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_key_prefix() != 35279.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_single_latest_per_key() != 58221.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_single_latest_per_key_exact() != 6734.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_query_single_latest_per_key_prefix() != 8914.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_readatlen_all() != 34450.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_readatlen_at_most() != 62414.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_readatlen_exact() != 12971.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_settagoption_auto() != 50496.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_settagoption_named() != 33009.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_wrapoption_no_wrap() != 59800.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } - if (lib.uniffi_iroh_ffi_checksum_constructor_wrapoption_wrap() != 6667.toShort()) { - throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") - } -} - -// Async support -// Async return type handlers - -internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toByte() -internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toByte() - -internal val uniffiContinuationHandleMap = UniffiHandleMap>() - -// FFI type for Rust future continuations -internal object uniffiRustFutureContinuationCallbackImpl : UniffiRustFutureContinuationCallback { - override fun callback( - data: Long, - pollResult: Byte, - ) { - uniffiContinuationHandleMap.remove(data).resume(pollResult) - } -} - -internal suspend fun uniffiRustCallAsync( - rustFuture: Long, - pollFunc: (Long, UniffiRustFutureContinuationCallback, Long) -> Unit, - completeFunc: (Long, UniffiRustCallStatus) -> F, - freeFunc: (Long) -> Unit, - liftFunc: (F) -> T, - errorHandler: UniffiRustCallStatusErrorHandler, -): T { - try { - do { - val pollResult = - suspendCancellableCoroutine { continuation -> - pollFunc( - rustFuture, - uniffiRustFutureContinuationCallbackImpl, - uniffiContinuationHandleMap.insert(continuation), - ) - } - } while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY) - - return liftFunc( - uniffiRustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) }), - ) - } finally { - freeFunc(rustFuture) - } -} - -internal inline fun uniffiTraitInterfaceCallAsync( - crossinline makeCall: suspend () -> T, - crossinline handleSuccess: (T) -> Unit, - crossinline handleError: (UniffiRustCallStatus.ByValue) -> Unit, -): UniffiForeignFuture { - // Using `GlobalScope` is labeled as a "delicate API" and generally discouraged in Kotlin programs, since it breaks structured concurrency. - // However, our parent task is a Rust future, so we're going to need to break structure concurrency in any case. - // - // Uniffi does its best to support structured concurrency across the FFI. - // If the Rust future is dropped, `uniffiForeignFutureFreeImpl` is called, which will cancel the Kotlin coroutine if it's still running. - @OptIn(DelicateCoroutinesApi::class) - val job = - GlobalScope.launch { - try { - handleSuccess(makeCall()) - } catch (e: Exception) { - handleError( - UniffiRustCallStatus.create( - UNIFFI_CALL_UNEXPECTED_ERROR, - FfiConverterString.lower(e.toString()), - ), - ) - } - } - val handle = uniffiForeignFutureHandleMap.insert(job) - return UniffiForeignFuture(handle, uniffiForeignFutureFreeImpl) -} - -internal inline fun uniffiTraitInterfaceCallAsyncWithError( - crossinline makeCall: suspend () -> T, - crossinline handleSuccess: (T) -> Unit, - crossinline handleError: (UniffiRustCallStatus.ByValue) -> Unit, - crossinline lowerError: (E) -> RustBuffer.ByValue, -): UniffiForeignFuture { - // See uniffiTraitInterfaceCallAsync for details on `DelicateCoroutinesApi` - @OptIn(DelicateCoroutinesApi::class) - val job = - GlobalScope.launch { - try { - handleSuccess(makeCall()) - } catch (e: Exception) { - if (e is E) { - handleError( - UniffiRustCallStatus.create( - UNIFFI_CALL_ERROR, - lowerError(e), - ), - ) - } else { - handleError( - UniffiRustCallStatus.create( - UNIFFI_CALL_UNEXPECTED_ERROR, - FfiConverterString.lower(e.toString()), - ), - ) - } - } - } - val handle = uniffiForeignFutureHandleMap.insert(job) - return UniffiForeignFuture(handle, uniffiForeignFutureFreeImpl) -} - -internal val uniffiForeignFutureHandleMap = UniffiHandleMap() - -internal object uniffiForeignFutureFreeImpl : UniffiForeignFutureFree { - override fun callback(handle: Long) { - val job = uniffiForeignFutureHandleMap.remove(handle) - if (!job.isCompleted) { - job.cancel() - } - } -} - -// For testing -public fun uniffiForeignFutureHandleCount() = uniffiForeignFutureHandleMap.size - -// Public interface members begin here. - -// Interface implemented by anything that can contain an object reference. -// -// Such types expose a `destroy()` method that must be called to cleanly -// dispose of the contained objects. Failure to call this method may result -// in memory leaks. -// -// The easiest way to ensure this method is called is to use the `.use` -// helper method to execute a block and destroy the object at the end. -interface Disposable { - fun destroy() - - companion object { - fun destroy(vararg args: Any?) { - args - .filterIsInstance() - .forEach(Disposable::destroy) - } - } -} - -/** - * @suppress - */ -inline fun T.use(block: (T) -> R) = - try { - block(this) - } finally { - try { - // N.B. our implementation is on the nullable type `Disposable?`. - this?.destroy() - } catch (e: Throwable) { - // swallow - } - } - -/** - * Used to instantiate an interface without an actual pointer, for fakes in tests, mostly. - * - * @suppress - * */ -object NoPointer - -/** - * @suppress - */ -public object FfiConverterUInt : FfiConverter { - override fun lift(value: Int): UInt = value.toUInt() - - override fun read(buf: ByteBuffer): UInt = lift(buf.getInt()) - - override fun lower(value: UInt): Int = value.toInt() - - override fun allocationSize(value: UInt) = 4UL - - override fun write( - value: UInt, - buf: ByteBuffer, - ) { - buf.putInt(value.toInt()) - } -} - -/** - * @suppress - */ -public object FfiConverterInt : FfiConverter { - override fun lift(value: Int): Int = value - - override fun read(buf: ByteBuffer): Int = buf.getInt() - - override fun lower(value: Int): Int = value - - override fun allocationSize(value: Int) = 4UL - - override fun write( - value: Int, - buf: ByteBuffer, - ) { - buf.putInt(value) - } -} - -/** - * @suppress - */ -public object FfiConverterULong : FfiConverter { - override fun lift(value: Long): ULong = value.toULong() - - override fun read(buf: ByteBuffer): ULong = lift(buf.getLong()) - - override fun lower(value: ULong): Long = value.toLong() - - override fun allocationSize(value: ULong) = 8UL - - override fun write( - value: ULong, - buf: ByteBuffer, - ) { - buf.putLong(value.toLong()) - } -} - -/** - * @suppress - */ -public object FfiConverterBoolean : FfiConverter { - override fun lift(value: Byte): Boolean = value.toInt() != 0 - - override fun read(buf: ByteBuffer): Boolean = lift(buf.get()) - - override fun lower(value: Boolean): Byte = if (value) 1.toByte() else 0.toByte() - - override fun allocationSize(value: Boolean) = 1UL - - override fun write( - value: Boolean, - buf: ByteBuffer, - ) { - buf.put(lower(value)) - } -} - -/** - * @suppress - */ -public object FfiConverterString : FfiConverter { - // Note: we don't inherit from FfiConverterRustBuffer, because we use a - // special encoding when lowering/lifting. We can use `RustBuffer.len` to - // store our length and avoid writing it out to the buffer. - override fun lift(value: RustBuffer.ByValue): String { - try { - val byteArr = ByteArray(value.len.toInt()) - value.asByteBuffer()!!.get(byteArr) - return byteArr.toString(Charsets.UTF_8) - } finally { - RustBuffer.free(value) - } - } - - override fun read(buf: ByteBuffer): String { - val len = buf.getInt() - val byteArr = ByteArray(len) - buf.get(byteArr) - return byteArr.toString(Charsets.UTF_8) - } - - fun toUtf8(value: String): ByteBuffer { - // Make sure we don't have invalid UTF-16, check for lone surrogates. - return Charsets.UTF_8.newEncoder().run { - onMalformedInput(CodingErrorAction.REPORT) - encode(CharBuffer.wrap(value)) - } - } - - override fun lower(value: String): RustBuffer.ByValue { - val byteBuf = toUtf8(value) - // Ideally we'd pass these bytes to `ffi_bytebuffer_from_bytes`, but doing so would require us - // to copy them into a JNA `Memory`. So we might as well directly copy them into a `RustBuffer`. - val rbuf = RustBuffer.alloc(byteBuf.limit().toULong()) - rbuf.asByteBuffer()!!.put(byteBuf) - return rbuf - } - - // We aren't sure exactly how many bytes our string will be once it's UTF-8 - // encoded. Allocate 3 bytes per UTF-16 code unit which will always be - // enough. - override fun allocationSize(value: String): ULong { - val sizeForLength = 4UL - val sizeForString = value.length.toULong() * 3UL - return sizeForLength + sizeForString - } - - override fun write( - value: String, - buf: ByteBuffer, - ) { - val byteBuf = toUtf8(value) - buf.putInt(byteBuf.limit()) - buf.put(byteBuf) - } -} - -/** - * @suppress - */ -public object FfiConverterByteArray : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ByteArray { - val len = buf.getInt() - val byteArr = ByteArray(len) - buf.get(byteArr) - return byteArr - } - - override fun allocationSize(value: ByteArray): ULong = 4UL + value.size.toULong() - - override fun write( - value: ByteArray, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - buf.put(value) - } -} - -/** - * @suppress - */ -public object FfiConverterTimestamp : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): java.time.Instant { - val seconds = buf.getLong() - // Type mismatch (should be u32) but we check for overflow/underflow below - val nanoseconds = buf.getInt().toLong() - if (nanoseconds < 0) { - throw java.time.DateTimeException("Instant nanoseconds exceed minimum or maximum supported by uniffi") - } - if (seconds >= 0) { - return java.time.Instant.EPOCH - .plus(java.time.Duration.ofSeconds(seconds, nanoseconds)) - } else { - return java.time.Instant.EPOCH - .minus(java.time.Duration.ofSeconds(-seconds, nanoseconds)) - } - } - - // 8 bytes for seconds, 4 bytes for nanoseconds - override fun allocationSize(value: java.time.Instant) = 12UL - - override fun write( - value: java.time.Instant, - buf: ByteBuffer, - ) { - var epochOffset = java.time.Duration.between(java.time.Instant.EPOCH, value) - - var sign = 1 - if (epochOffset.isNegative()) { - sign = -1 - epochOffset = epochOffset.negated() - } - - if (epochOffset.nano < 0) { - // Java docs provide guarantee that nano will always be positive, so this should be impossible - // See: https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html - throw IllegalArgumentException("Invalid timestamp, nano value must be non-negative") - } - - buf.putLong(sign * epochOffset.seconds) - // Type mismatch (should be u32) but since values will always be between 0 and 999,999,999 it should be OK - buf.putInt(epochOffset.nano) - } -} - -/** - * @suppress - */ -public object FfiConverterDuration : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): java.time.Duration { - // Type mismatch (should be u64) but we check for overflow/underflow below - val seconds = buf.getLong() - // Type mismatch (should be u32) but we check for overflow/underflow below - val nanoseconds = buf.getInt().toLong() - if (seconds < 0) { - throw java.time.DateTimeException("Duration exceeds minimum or maximum value supported by uniffi") - } - if (nanoseconds < 0) { - throw java.time.DateTimeException("Duration nanoseconds exceed minimum or maximum supported by uniffi") - } - return java.time.Duration.ofSeconds(seconds, nanoseconds) - } - - // 8 bytes for seconds, 4 bytes for nanoseconds - override fun allocationSize(value: java.time.Duration) = 12UL - - override fun write( - value: java.time.Duration, - buf: ByteBuffer, - ) { - if (value.seconds < 0) { - // Rust does not support negative Durations - throw IllegalArgumentException("Invalid duration, must be non-negative") - } - - if (value.nano < 0) { - // Java docs provide guarantee that nano will always be positive, so this should be impossible - // See: https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html - throw IllegalArgumentException("Invalid duration, nano value must be non-negative") - } - - // Type mismatch (should be u64) but since Rust doesn't support negative durations we should be OK - buf.putLong(value.seconds) - // Type mismatch (should be u32) but since values will always be between 0 and 999,999,999 it should be OK - buf.putInt(value.nano) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The cleaner interface for Object finalization code to run. - * This is the entry point to any implementation that we're using. - * - * The cleaner registers objects and returns cleanables, so now we are - * defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the - * different implmentations available at compile time. - * - * @suppress - */ -interface UniffiCleaner { - interface Cleanable { - fun clean() - } - - fun register( - value: Any, - cleanUpTask: Runnable, - ): UniffiCleaner.Cleanable - - companion object -} - -// The fallback Jna cleaner, which is available for both Android, and the JVM. -private class UniffiJnaCleaner : UniffiCleaner { - private val cleaner = - com.sun.jna.internal.Cleaner - .getCleaner() - - override fun register( - value: Any, - cleanUpTask: Runnable, - ): UniffiCleaner.Cleanable = UniffiJnaCleanable(cleaner.register(value, cleanUpTask)) -} - -private class UniffiJnaCleanable( - private val cleanable: com.sun.jna.internal.Cleaner.Cleanable, -) : UniffiCleaner.Cleanable { - override fun clean() = cleanable.clean() -} - -// We decide at uniffi binding generation time whether we were -// using Android or not. -// There are further runtime checks to chose the correct implementation -// of the cleaner. -private fun UniffiCleaner.Companion.create(): UniffiCleaner = - try { - // For safety's sake: if the library hasn't been run in android_cleaner = true - // mode, but is being run on Android, then we still need to think about - // Android API versions. - // So we check if java.lang.ref.Cleaner is there, and use that… - java.lang.Class.forName("java.lang.ref.Cleaner") - JavaLangRefCleaner() - } catch (e: ClassNotFoundException) { - // … otherwise, fallback to the JNA cleaner. - UniffiJnaCleaner() - } - -private class JavaLangRefCleaner : UniffiCleaner { - val cleaner = - java.lang.ref.Cleaner - .create() - - override fun register( - value: Any, - cleanUpTask: Runnable, - ): UniffiCleaner.Cleanable = JavaLangRefCleanable(cleaner.register(value, cleanUpTask)) -} - -private class JavaLangRefCleanable( - val cleanable: java.lang.ref.Cleaner.Cleanable, -) : UniffiCleaner.Cleanable { - override fun clean() = cleanable.clean() -} - -/** - * The `progress` method will be called for each `AddProgress` event that is - * emitted during a `node.blobs_add_from_path`. Use the `AddProgress.type()` - * method to check the `AddProgressType` - */ -public interface AddCallback { - suspend fun `progress`(`progress`: AddProgress) - - companion object -} - -/** - * The `progress` method will be called for each `AddProgress` event that is - * emitted during a `node.blobs_add_from_path`. Use the `AddProgress.type()` - * method to check the `AddProgressType` - */ -open class AddCallbackImpl : - Disposable, - AutoCloseable, - AddCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_addcallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_addcallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `progress`(`progress`: AddProgress) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addcallback_progress( - thisPtr, - FfiConverterTypeAddProgress.lower(`progress`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Magic number for the Rust proxy to call using the same mechanism as every other method, -// to free the callback once it's dropped by Rust. -internal const val IDX_CALLBACK_FREE = 0 - -// Callback return codes -internal const val UNIFFI_CALLBACK_SUCCESS = 0 -internal const val UNIFFI_CALLBACK_ERROR = 1 -internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2 - -/** - * @suppress - */ -public abstract class FfiConverterCallbackInterface : FfiConverter { - internal val handleMap = UniffiHandleMap() - - internal fun drop(handle: Long) { - handleMap.remove(handle) - } - - override fun lift(value: Long): CallbackInterface = handleMap.get(value) - - override fun read(buf: ByteBuffer) = lift(buf.getLong()) - - override fun lower(value: CallbackInterface) = handleMap.insert(value) - - override fun allocationSize(value: CallbackInterface) = 8UL - - override fun write( - value: CallbackInterface, - buf: ByteBuffer, - ) { - buf.putLong(lower(value)) - } -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceAddCallback { - internal object `progress` : UniffiCallbackInterfaceAddCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeAddCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`progress`( - FfiConverterTypeAddProgress.lift(`progress`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeAddCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceAddCallback.UniffiByValue( - `progress`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_addcallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeAddCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: AddCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): AddCallback = AddCallbackImpl(value) - - override fun read(buf: ByteBuffer): AddCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: AddCallback) = 8UL - - override fun write( - value: AddCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Progress updates for the add operation. - */ -public interface AddProgressInterface { - /** - * Return the `AddProgressAbort` - */ - fun `asAbort`(): AddProgressAbort - - /** - * Return the `AddAllDone` - */ - fun `asAllDone`(): AddProgressAllDone - - /** - * Return the `AddProgressDone` event - */ - fun `asDone`(): AddProgressDone - - /** - * Return the `AddProgressFound` event - */ - fun `asFound`(): AddProgressFound - - /** - * Return the `AddProgressProgress` event - */ - fun `asProgress`(): AddProgressProgress - - /** - * Get the type of event - */ - fun `type`(): AddProgressType - - companion object -} - -/** - * Progress updates for the add operation. - */ -open class AddProgress : - Disposable, - AutoCloseable, - AddProgressInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_addprogress(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_addprogress(pointer!!, status) - } - - /** - * Return the `AddProgressAbort` - */ - override fun `asAbort`(): AddProgressAbort = - FfiConverterTypeAddProgressAbort.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addprogress_as_abort( - it, - _status, - ) - } - }, - ) - - /** - * Return the `AddAllDone` - */ - override fun `asAllDone`(): AddProgressAllDone = - FfiConverterTypeAddProgressAllDone.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addprogress_as_all_done( - it, - _status, - ) - } - }, - ) - - /** - * Return the `AddProgressDone` event - */ - override fun `asDone`(): AddProgressDone = - FfiConverterTypeAddProgressDone.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addprogress_as_done( - it, - _status, - ) - } - }, - ) - - /** - * Return the `AddProgressFound` event - */ - override fun `asFound`(): AddProgressFound = - FfiConverterTypeAddProgressFound.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addprogress_as_found( - it, - _status, - ) - } - }, - ) - - /** - * Return the `AddProgressProgress` event - */ - override fun `asProgress`(): AddProgressProgress = - FfiConverterTypeAddProgressProgress.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addprogress_as_progress( - it, - _status, - ) - } - }, - ) - - /** - * Get the type of event - */ - override fun `type`(): AddProgressType = - FfiConverterTypeAddProgressType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_addprogress_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgress : FfiConverter { - override fun lower(value: AddProgress): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): AddProgress = AddProgress(value) - - override fun read(buf: ByteBuffer): AddProgress { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: AddProgress) = 8UL - - override fun write( - value: AddProgress, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Author key to insert entries in a document - * - * Internally, an author is a `SigningKey` which is used to sign entries. - */ -public interface AuthorInterface { - /** - * Get the [`AuthorId`] of this Author - */ - fun `id`(): AuthorId - - companion object -} - -/** - * Author key to insert entries in a document - * - * Internally, an author is a `SigningKey` which is used to sign entries. - */ -open class Author : - Disposable, - AutoCloseable, - AuthorInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_author(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_author(pointer!!, status) - } - - /** - * Get the [`AuthorId`] of this Author - */ - override fun `id`(): AuthorId = - FfiConverterTypeAuthorId.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_author_id( - it, - _status, - ) - } - }, - ) - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_author_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Get an [`Author`] from a String - */ - @Throws(IrohException::class) - fun `fromString`(`str`: kotlin.String): Author = - FfiConverterTypeAuthor.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_author_from_string( - FfiConverterString.lower(`str`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeAuthor : FfiConverter { - override fun lower(value: Author): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Author = Author(value) - - override fun read(buf: ByteBuffer): Author { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Author) = 8UL - - override fun write( - value: Author, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Identifier for an [`Author`] - */ -public interface AuthorIdInterface { - /** - * Returns true when both AuthorId's have the same value - */ - fun `equal`(`other`: AuthorId): kotlin.Boolean - - companion object -} - -/** - * Identifier for an [`Author`] - */ -open class AuthorId : - Disposable, - AutoCloseable, - AuthorIdInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_authorid(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_authorid(pointer!!, status) - } - - /** - * Returns true when both AuthorId's have the same value - */ - override fun `equal`(`other`: AuthorId): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authorid_equal( - it, - FfiConverterTypeAuthorId.lower(`other`), - _status, - ) - } - }, - ) - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authorid_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Get an [`AuthorId`] from a String. - */ - @Throws(IrohException::class) - fun `fromString`(`str`: kotlin.String): AuthorId = - FfiConverterTypeAuthorId.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_authorid_from_string( - FfiConverterString.lower(`str`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeAuthorId : FfiConverter { - override fun lower(value: AuthorId): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): AuthorId = AuthorId(value) - - override fun read(buf: ByteBuffer): AuthorId { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: AuthorId) = 8UL - - override fun write( - value: AuthorId, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh authors client. - */ -public interface AuthorsInterface { - /** - * Create a new document author. - * - * You likely want to save the returned [`AuthorId`] somewhere so that you can use this author - * again. - * - * If you need only a single author, use [`Self::default`]. - */ - suspend fun `create`(): AuthorId - - /** - * Returns the default document author of this node. - * - * On persistent nodes, the author is created on first start and its public key is saved - * in the data directory. - * - * The default author can be set with [`Self::set_default`]. - */ - suspend fun `default`(): AuthorId - - /** - * Deletes the given author by id. - * - * Warning: This permanently removes this author. - */ - suspend fun `delete`(`author`: AuthorId) - - /** - * Export the given author. - * - * Warning: This contains sensitive data. - */ - suspend fun `export`(`author`: AuthorId): Author - - /** - * Import the given author. - * - * Warning: This contains sensitive data. - */ - suspend fun `import`(`author`: Author): AuthorId - - /** - * Import the given author. - * - * Warning: This contains sensitive data. - * `import` is reserved in python. - */ - suspend fun `importAuthor`(`author`: Author): AuthorId - - /** - * List all the AuthorIds that exist on this node. - */ - suspend fun `list`(): List - - companion object -} - -/** - * Iroh authors client. - */ -open class Authors : - Disposable, - AutoCloseable, - AuthorsInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_authors(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_authors(pointer!!, status) - } - - /** - * Create a new document author. - * - * You likely want to save the returned [`AuthorId`] somewhere so that you can use this author - * again. - * - * If you need only a single author, use [`Self::default`]. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `create`(): AuthorId = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_create( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeAuthorId.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Returns the default document author of this node. - * - * On persistent nodes, the author is created on first start and its public key is saved - * in the data directory. - * - * The default author can be set with [`Self::set_default`]. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `default`(): AuthorId = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_default( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeAuthorId.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Deletes the given author by id. - * - * Warning: This permanently removes this author. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `delete`(`author`: AuthorId) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_delete( - thisPtr, - FfiConverterTypeAuthorId.lower(`author`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Export the given author. - * - * Warning: This contains sensitive data. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `export`(`author`: AuthorId): Author = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_export( - thisPtr, - FfiConverterTypeAuthorId.lower(`author`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeAuthor.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Import the given author. - * - * Warning: This contains sensitive data. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `import`(`author`: Author): AuthorId = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_import( - thisPtr, - FfiConverterTypeAuthor.lower(`author`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeAuthorId.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Import the given author. - * - * Warning: This contains sensitive data. - * `import` is reserved in python. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `importAuthor`(`author`: Author): AuthorId = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_import_author( - thisPtr, - FfiConverterTypeAuthor.lower(`author`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeAuthorId.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * List all the AuthorIds that exist on this node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `list`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_authors_list( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeAuthorId.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAuthors : FfiConverter { - override fun lower(value: Authors): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Authors = Authors(value) - - override fun read(buf: ByteBuffer): Authors { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Authors) = 8UL - - override fun write( - value: Authors, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface BiStreamInterface { - fun `recv`(): RecvStream - - fun `send`(): SendStream - - companion object -} - -open class BiStream : - Disposable, - AutoCloseable, - BiStreamInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_bistream(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_bistream(pointer!!, status) - } - - override fun `recv`(): RecvStream = - FfiConverterTypeRecvStream.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_bistream_recv( - it, - _status, - ) - } - }, - ) - - override fun `send`(): SendStream = - FfiConverterTypeSendStream.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_bistream_send( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBiStream : FfiConverter { - override fun lower(value: BiStream): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): BiStream = BiStream(value) - - override fun read(buf: ByteBuffer): BiStream { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: BiStream) = 8UL - - override fun write( - value: BiStream, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Options to download data specified by the hash. - */ -public interface BlobDownloadOptionsInterface { - companion object -} - -/** - * Options to download data specified by the hash. - */ -open class BlobDownloadOptions : - Disposable, - AutoCloseable, - BlobDownloadOptionsInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * Create a BlobDownloadRequest - */ - constructor(`format`: BlobFormat, `nodes`: List, `tag`: SetTagOption) : - this( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_blobdownloadoptions_new( - FfiConverterTypeBlobFormat.lower(`format`), - FfiConverterSequenceTypeNodeAddr.lower(`nodes`), - FfiConverterTypeSetTagOption.lower(`tag`), - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_blobdownloadoptions(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_blobdownloadoptions(pointer!!, status) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobDownloadOptions : FfiConverter { - override fun lower(value: BlobDownloadOptions): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): BlobDownloadOptions = BlobDownloadOptions(value) - - override fun read(buf: ByteBuffer): BlobDownloadOptions { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: BlobDownloadOptions) = 8UL - - override fun write( - value: BlobDownloadOptions, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Events emitted by the provider informing about the current status. - */ -public interface BlobProvideEventInterface { - /** - * Return the `ClientConnected` event - */ - fun `asClientConnected`(): ClientConnected - - /** - * Return the `GetRequestReceived` event - */ - fun `asGetRequestReceived`(): GetRequestReceived - - /** - * Return the `TaggedBlobAdded` event - */ - fun `asTaggedBlobAdded`(): TaggedBlobAdded - - /** - * Return the `TransferAborted` event - */ - fun `asTransferAborted`(): TransferAborted - - /** - * Return the `TransferBlobCompleted` event - */ - fun `asTransferBlobCompleted`(): TransferBlobCompleted - - /** - * Return the `TransferCompleted` event - */ - fun `asTransferCompleted`(): TransferCompleted - - /** - * Return the `TransferHashSeqStarted` event - */ - fun `asTransferHashSeqStarted`(): TransferHashSeqStarted - - /** - * Return the `TransferProgress` event - */ - fun `asTransferProgress`(): TransferProgress - - /** - * Get the type of event - */ - fun `type`(): BlobProvideEventType - - companion object -} - -/** - * Events emitted by the provider informing about the current status. - */ -open class BlobProvideEvent : - Disposable, - AutoCloseable, - BlobProvideEventInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_blobprovideevent(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_blobprovideevent(pointer!!, status) - } - - /** - * Return the `ClientConnected` event - */ - override fun `asClientConnected`(): ClientConnected = - FfiConverterTypeClientConnected.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_client_connected( - it, - _status, - ) - } - }, - ) - - /** - * Return the `GetRequestReceived` event - */ - override fun `asGetRequestReceived`(): GetRequestReceived = - FfiConverterTypeGetRequestReceived.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_get_request_received( - it, - _status, - ) - } - }, - ) - - /** - * Return the `TaggedBlobAdded` event - */ - override fun `asTaggedBlobAdded`(): TaggedBlobAdded = - FfiConverterTypeTaggedBlobAdded.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_tagged_blob_added( - it, - _status, - ) - } - }, - ) - - /** - * Return the `TransferAborted` event - */ - override fun `asTransferAborted`(): TransferAborted = - FfiConverterTypeTransferAborted.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_aborted( - it, - _status, - ) - } - }, - ) - - /** - * Return the `TransferBlobCompleted` event - */ - override fun `asTransferBlobCompleted`(): TransferBlobCompleted = - FfiConverterTypeTransferBlobCompleted.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_blob_completed( - it, - _status, - ) - } - }, - ) - - /** - * Return the `TransferCompleted` event - */ - override fun `asTransferCompleted`(): TransferCompleted = - FfiConverterTypeTransferCompleted.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_completed( - it, - _status, - ) - } - }, - ) - - /** - * Return the `TransferHashSeqStarted` event - */ - override fun `asTransferHashSeqStarted`(): TransferHashSeqStarted = - FfiConverterTypeTransferHashSeqStarted.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_hash_seq_started( - it, - _status, - ) - } - }, - ) - - /** - * Return the `TransferProgress` event - */ - override fun `asTransferProgress`(): TransferProgress = - FfiConverterTypeTransferProgress.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_as_transfer_progress( - it, - _status, - ) - } - }, - ) - - /** - * Get the type of event - */ - override fun `type`(): BlobProvideEventType = - FfiConverterTypeBlobProvideEventType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideevent_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobProvideEvent : FfiConverter { - override fun lower(value: BlobProvideEvent): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): BlobProvideEvent = BlobProvideEvent(value) - - override fun read(buf: ByteBuffer): BlobProvideEvent { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: BlobProvideEvent) = 8UL - - override fun write( - value: BlobProvideEvent, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The `progress` method will be called for each `BlobProvideEvent` event that is - * emitted from the iroh node while the callback is registered. Use the `BlobProvideEvent.type()` - * method to check the `BlobProvideEventType` - */ -public interface BlobProvideEventCallback { - suspend fun `blobEvent`(`event`: BlobProvideEvent) - - companion object -} - -/** - * The `progress` method will be called for each `BlobProvideEvent` event that is - * emitted from the iroh node while the callback is registered. Use the `BlobProvideEvent.type()` - * method to check the `BlobProvideEventType` - */ -open class BlobProvideEventCallbackImpl : - Disposable, - AutoCloseable, - BlobProvideEventCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_blobprovideeventcallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_blobprovideeventcallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `blobEvent`(`event`: BlobProvideEvent) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobprovideeventcallback_blob_event( - thisPtr, - FfiConverterTypeBlobProvideEvent.lower(`event`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceBlobProvideEventCallback { - internal object `blobEvent` : UniffiCallbackInterfaceBlobProvideEventCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `event`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeBlobProvideEventCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`blobEvent`( - FfiConverterTypeBlobProvideEvent.lift(`event`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeBlobProvideEventCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceBlobProvideEventCallback.UniffiByValue( - `blobEvent`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_blobprovideeventcallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobProvideEventCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: BlobProvideEventCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): BlobProvideEventCallback = BlobProvideEventCallbackImpl(value) - - override fun read(buf: ByteBuffer): BlobProvideEventCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: BlobProvideEventCallback) = 8UL - - override fun write( - value: BlobProvideEventCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Status information about a blob. - */ -public interface BlobStatusInterface { - companion object -} - -/** - * Status information about a blob. - */ -open class BlobStatus : - Disposable, - AutoCloseable, - BlobStatusInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_blobstatus(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_blobstatus(pointer!!, status) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobStatus : FfiConverter { - override fun lower(value: BlobStatus): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): BlobStatus = BlobStatus(value) - - override fun read(buf: ByteBuffer): BlobStatus { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: BlobStatus) = 8UL - - override fun write( - value: BlobStatus, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A token containing everything to get a file from the provider. - * - * It is a single item which can be easily serialized and deserialized. - */ -public interface BlobTicketInterface { - /** - * Convert this ticket into input parameters for a call to blobs_download - */ - fun `asDownloadOptions`(): BlobDownloadOptions - - /** - * The [`BlobFormat`] for this ticket. - */ - fun `format`(): BlobFormat - - /** - * The hash of the item this ticket can retrieve. - */ - fun `hash`(): Hash - - /** - * The [`NodeAddr`] of the provider for this ticket. - */ - fun `nodeAddr`(): NodeAddr - - /** - * True if the ticket is for a collection and should retrieve all blobs in it. - */ - fun `recursive`(): kotlin.Boolean - - companion object -} - -/** - * A token containing everything to get a file from the provider. - * - * It is a single item which can be easily serialized and deserialized. - */ -open class BlobTicket : - Disposable, - AutoCloseable, - BlobTicketInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - constructor(`str`: kotlin.String) : - this( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_blobticket_new( - FfiConverterString.lower(`str`), - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_blobticket(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_blobticket(pointer!!, status) - } - - /** - * Convert this ticket into input parameters for a call to blobs_download - */ - override fun `asDownloadOptions`(): BlobDownloadOptions = - FfiConverterTypeBlobDownloadOptions.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobticket_as_download_options( - it, - _status, - ) - } - }, - ) - - /** - * The [`BlobFormat`] for this ticket. - */ - override fun `format`(): BlobFormat = - FfiConverterTypeBlobFormat.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobticket_format( - it, - _status, - ) - } - }, - ) - - /** - * The hash of the item this ticket can retrieve. - */ - override fun `hash`(): Hash = - FfiConverterTypeHash.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobticket_hash( - it, - _status, - ) - } - }, - ) - - /** - * The [`NodeAddr`] of the provider for this ticket. - */ - override fun `nodeAddr`(): NodeAddr = - FfiConverterTypeNodeAddr.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobticket_node_addr( - it, - _status, - ) - } - }, - ) - - /** - * True if the ticket is for a collection and should retrieve all blobs in it. - */ - override fun `recursive`(): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobticket_recursive( - it, - _status, - ) - } - }, - ) - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobticket_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobTicket : FfiConverter { - override fun lower(value: BlobTicket): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): BlobTicket = BlobTicket(value) - - override fun read(buf: ByteBuffer): BlobTicket { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: BlobTicket) = 8UL - - override fun write( - value: BlobTicket, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh blobs client. - */ -public interface BlobsInterface { - /** - * Write a blob by passing bytes. - */ - suspend fun `addBytes`(`bytes`: kotlin.ByteArray): BlobAddOutcome - - /** - * Write a blob by passing bytes, setting an explicit tag name. - */ - suspend fun `addBytesNamed`( - `bytes`: kotlin.ByteArray, - `name`: kotlin.String, - ): BlobAddOutcome - - /** - * Import a blob from a filesystem path. - * - * `path` should be an absolute path valid for the file system on which - * the node runs. - * If `in_place` is true, Iroh will assume that the data will not change and will share it in - * place without copying to the Iroh data directory. - */ - suspend fun `addFromPath`( - `path`: kotlin.String, - `inPlace`: kotlin.Boolean, - `tag`: SetTagOption, - `wrap`: WrapOption, - `cb`: AddCallback, - ) - - /** - * Create a collection from already existing blobs. - * - * To automatically clear the tags for the passed in blobs you can set - * `tags_to_delete` on those tags, and they will be deleted once the collection is created. - */ - suspend fun `createCollection`( - `collection`: Collection, - `tag`: SetTagOption, - `tagsToDelete`: List, - ): HashAndTag - - /** - * Delete a blob. - */ - suspend fun `deleteBlob`(`hash`: Hash) - - /** - * Download a blob from another node and add it to the local database. - */ - suspend fun `download`( - `hash`: Hash, - `opts`: BlobDownloadOptions, - `cb`: DownloadCallback, - ) - - /** - * Export a blob from the internal blob store to a path on the node's filesystem. - * - * `destination` should be a writeable, absolute path on the local node's filesystem. - * - * If `format` is set to [`ExportFormat::Collection`], and the `hash` refers to a collection, - * all children of the collection will be exported. See [`ExportFormat`] for details. - * - * The `mode` argument defines if the blob should be copied to the target location or moved out of - * the internal store into the target location. See [`ExportMode`] for details. - */ - suspend fun `export`( - `hash`: Hash, - `destination`: kotlin.String, - `format`: BlobExportFormat, - `mode`: BlobExportMode, - ) - - /** - * Read the content of a collection - */ - suspend fun `getCollection`(`hash`: Hash): Collection - - /** - * Check if a blob is completely stored on the node. - * - * This is just a convenience wrapper around `status` that returns a boolean. - */ - suspend fun `has`(`hash`: Hash): kotlin.Boolean - - /** - * List all complete blobs. - * - * Note: this allocates for each `BlobListResponse`, if you have many `BlobListReponse`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - suspend fun `list`(): List - - /** - * List all collections. - * - * Note: this allocates for each `BlobListCollectionsResponse`, if you have many `BlobListCollectionsResponse`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - suspend fun `listCollections`(): List - - /** - * List all incomplete (partial) blobs. - * - * Note: this allocates for each `BlobListIncompleteResponse`, if you have many `BlobListIncompleteResponse`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - suspend fun `listIncomplete`(): List - - /** - * Read all bytes of single blob at `offset` for length `len`. - * - * This allocates a buffer for the full length `len`. Use only if you know that the blob you're - * reading is small. If not sure, use [`Self::blobs_size`] and check the size with - * before calling [`Self::blobs_read_at_to_bytes`]. - */ - suspend fun `readAtToBytes`( - `hash`: Hash, - `offset`: kotlin.ULong, - `len`: ReadAtLen, - ): kotlin.ByteArray - - /** - * Read all bytes of single blob. - * - * This allocates a buffer for the full blob. Use only if you know that the blob you're - * reading is small. If not sure, use [`Self::blobs_size`] and check the size with - * before calling [`Self::blobs_read_to_bytes`]. - */ - suspend fun `readToBytes`(`hash`: Hash): kotlin.ByteArray - - /** - * Create a ticket for sharing a blob from this node. - */ - suspend fun `share`( - `hash`: Hash, - `blobFormat`: BlobFormat, - `ticketOptions`: AddrInfoOptions, - ): BlobTicket - - /** - * Get the size information on a single blob. - * - * Method only exists in FFI - */ - suspend fun `size`(`hash`: Hash): kotlin.ULong - - /** - * Check the storage status of a blob on this node. - */ - suspend fun `status`(`hash`: Hash): BlobStatus - - /** - * Export the blob contents to a file path - * The `path` field is expected to be the absolute path. - */ - suspend fun `writeToPath`( - `hash`: Hash, - `path`: kotlin.String, - ) - - companion object -} - -/** - * Iroh blobs client. - */ -open class Blobs : - Disposable, - AutoCloseable, - BlobsInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_blobs(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_blobs(pointer!!, status) - } - - /** - * Write a blob by passing bytes. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `addBytes`(`bytes`: kotlin.ByteArray): BlobAddOutcome = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_add_bytes( - thisPtr, - FfiConverterByteArray.lower(`bytes`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeBlobAddOutcome.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Write a blob by passing bytes, setting an explicit tag name. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `addBytesNamed`( - `bytes`: kotlin.ByteArray, - `name`: kotlin.String, - ): BlobAddOutcome = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_add_bytes_named( - thisPtr, - FfiConverterByteArray.lower(`bytes`), - FfiConverterString.lower(`name`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeBlobAddOutcome.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Import a blob from a filesystem path. - * - * `path` should be an absolute path valid for the file system on which - * the node runs. - * If `in_place` is true, Iroh will assume that the data will not change and will share it in - * place without copying to the Iroh data directory. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `addFromPath`( - `path`: kotlin.String, - `inPlace`: kotlin.Boolean, - `tag`: SetTagOption, - `wrap`: WrapOption, - `cb`: AddCallback, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_add_from_path( - thisPtr, - FfiConverterString.lower(`path`), - FfiConverterBoolean.lower(`inPlace`), - FfiConverterTypeSetTagOption.lower(`tag`), - FfiConverterTypeWrapOption.lower(`wrap`), - FfiConverterTypeAddCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Create a collection from already existing blobs. - * - * To automatically clear the tags for the passed in blobs you can set - * `tags_to_delete` on those tags, and they will be deleted once the collection is created. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `createCollection`( - `collection`: Collection, - `tag`: SetTagOption, - `tagsToDelete`: List, - ): HashAndTag = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_create_collection( - thisPtr, - FfiConverterTypeCollection.lower(`collection`), - FfiConverterTypeSetTagOption.lower(`tag`), - FfiConverterSequenceString.lower(`tagsToDelete`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeHashAndTag.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Delete a blob. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `deleteBlob`(`hash`: Hash) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_delete_blob( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Download a blob from another node and add it to the local database. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `download`( - `hash`: Hash, - `opts`: BlobDownloadOptions, - `cb`: DownloadCallback, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_download( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - FfiConverterTypeBlobDownloadOptions.lower(`opts`), - FfiConverterTypeDownloadCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Export a blob from the internal blob store to a path on the node's filesystem. - * - * `destination` should be a writeable, absolute path on the local node's filesystem. - * - * If `format` is set to [`ExportFormat::Collection`], and the `hash` refers to a collection, - * all children of the collection will be exported. See [`ExportFormat`] for details. - * - * The `mode` argument defines if the blob should be copied to the target location or moved out of - * the internal store into the target location. See [`ExportMode`] for details. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `export`( - `hash`: Hash, - `destination`: kotlin.String, - `format`: BlobExportFormat, - `mode`: BlobExportMode, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_export( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - FfiConverterString.lower(`destination`), - FfiConverterTypeBlobExportFormat.lower(`format`), - FfiConverterTypeBlobExportMode.lower(`mode`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Read the content of a collection - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getCollection`(`hash`: Hash): Collection = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_get_collection( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeCollection.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Check if a blob is completely stored on the node. - * - * This is just a convenience wrapper around `status` that returns a boolean. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `has`(`hash`: Hash): kotlin.Boolean = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_has( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_i8(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_i8(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_i8(future) }, - // lift function - { FfiConverterBoolean.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * List all complete blobs. - * - * Note: this allocates for each `BlobListResponse`, if you have many `BlobListReponse`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `list`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_list( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeHash.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * List all collections. - * - * Note: this allocates for each `BlobListCollectionsResponse`, if you have many `BlobListCollectionsResponse`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `listCollections`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_list_collections( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeCollectionInfo.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * List all incomplete (partial) blobs. - * - * Note: this allocates for each `BlobListIncompleteResponse`, if you have many `BlobListIncompleteResponse`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `listIncomplete`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_list_incomplete( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeIncompleteBlobInfo.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Read all bytes of single blob at `offset` for length `len`. - * - * This allocates a buffer for the full length `len`. Use only if you know that the blob you're - * reading is small. If not sure, use [`Self::blobs_size`] and check the size with - * before calling [`Self::blobs_read_at_to_bytes`]. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `readAtToBytes`( - `hash`: Hash, - `offset`: kotlin.ULong, - `len`: ReadAtLen, - ): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_read_at_to_bytes( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - FfiConverterULong.lower(`offset`), - FfiConverterTypeReadAtLen.lower(`len`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Read all bytes of single blob. - * - * This allocates a buffer for the full blob. Use only if you know that the blob you're - * reading is small. If not sure, use [`Self::blobs_size`] and check the size with - * before calling [`Self::blobs_read_to_bytes`]. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `readToBytes`(`hash`: Hash): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_read_to_bytes( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Create a ticket for sharing a blob from this node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `share`( - `hash`: Hash, - `blobFormat`: BlobFormat, - `ticketOptions`: AddrInfoOptions, - ): BlobTicket = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_share( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - FfiConverterTypeBlobFormat.lower(`blobFormat`), - FfiConverterTypeAddrInfoOptions.lower(`ticketOptions`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeBlobTicket.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get the size information on a single blob. - * - * Method only exists in FFI - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `size`(`hash`: Hash): kotlin.ULong = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_size( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterULong.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Check the storage status of a blob on this node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `status`(`hash`: Hash): BlobStatus = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_status( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeBlobStatus.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Export the blob contents to a file path - * The `path` field is expected to be the absolute path. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `writeToPath`( - `hash`: Hash, - `path`: kotlin.String, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_blobs_write_to_path( - thisPtr, - FfiConverterTypeHash.lower(`hash`), - FfiConverterString.lower(`path`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobs : FfiConverter { - override fun lower(value: Blobs): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Blobs = Blobs(value) - - override fun read(buf: ByteBuffer): Blobs { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Blobs) = 8UL - - override fun write( - value: Blobs, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A collection of blobs - */ -public interface CollectionInterface { - /** - * Get the blobs associated with this collection - */ - fun `blobs`(): List - - /** - * Check if the collection is empty - */ - fun `isEmpty`(): kotlin.Boolean - - /** - * Returns the number of blobs in this collection - */ - fun `len`(): kotlin.ULong - - /** - * Get the links to the blobs in this collection - */ - fun `links`(): List - - /** - * Get the names of the blobs in this collection - */ - fun `names`(): List - - /** - * Add the given blob to the collection - */ - fun `push`( - `name`: kotlin.String, - `hash`: Hash, - ) - - companion object -} - -/** - * A collection of blobs - */ -open class Collection : - Disposable, - AutoCloseable, - CollectionInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * Create a new empty collection - */ - constructor() : - this( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_collection_new( - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_collection(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_collection(pointer!!, status) - } - - /** - * Get the blobs associated with this collection - */ - @Throws(IrohException::class) - override fun `blobs`(): List = - FfiConverterSequenceTypeLinkAndName.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_collection_blobs( - it, - _status, - ) - } - }, - ) - - /** - * Check if the collection is empty - */ - @Throws(IrohException::class) - override fun `isEmpty`(): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_collection_is_empty( - it, - _status, - ) - } - }, - ) - - /** - * Returns the number of blobs in this collection - */ - @Throws(IrohException::class) - override fun `len`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_collection_len( - it, - _status, - ) - } - }, - ) - - /** - * Get the links to the blobs in this collection - */ - @Throws(IrohException::class) - override fun `links`(): List = - FfiConverterSequenceTypeHash.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_collection_links( - it, - _status, - ) - } - }, - ) - - /** - * Get the names of the blobs in this collection - */ - @Throws(IrohException::class) - override fun `names`(): List = - FfiConverterSequenceString.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_collection_names( - it, - _status, - ) - } - }, - ) - - /** - * Add the given blob to the collection - */ - @Throws(IrohException::class) - override fun `push`( - `name`: kotlin.String, - `hash`: Hash, - ) = callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_collection_push( - it, - FfiConverterString.lower(`name`), - FfiConverterTypeHash.lower(`hash`), - _status, - ) - } - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeCollection : FfiConverter { - override fun lower(value: Collection): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Collection = Collection(value) - - override fun read(buf: ByteBuffer): Collection { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Collection) = 8UL - - override fun write( - value: Collection, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface ConnectingInterface { - suspend fun `alpn`(): kotlin.ByteArray - - suspend fun `connect`(): Connection - - suspend fun `localIp`(): kotlin.String? - - suspend fun `remoteAddress`(): kotlin.String - - companion object -} - -open class Connecting : - Disposable, - AutoCloseable, - ConnectingInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_connecting(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_connecting(pointer!!, status) - } - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `alpn`(): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connecting_alpn( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `connect`(): Connection = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connecting_connect( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeConnection.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `localIp`(): kotlin.String? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connecting_local_ip( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalString.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `remoteAddress`(): kotlin.String = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connecting_remote_address( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterString.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeConnecting : FfiConverter { - override fun lower(value: Connecting): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Connecting = Connecting(value) - - override fun read(buf: ByteBuffer): Connecting { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Connecting) = 8UL - - override fun write( - value: Connecting, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface ConnectionInterface { - suspend fun `acceptBi`(): BiStream - - suspend fun `acceptUni`(): RecvStream - - fun `close`( - `errorCode`: kotlin.ULong, - `reason`: kotlin.ByteArray, - ) - - fun `closeReason`(): kotlin.String? - - suspend fun `closed`(): kotlin.String - - fun `datagramSendBufferSpace`(): kotlin.ULong - - fun `getRemoteNodeId`(): PublicKey - - fun `localIp`(): kotlin.String? - - fun `maxDatagramSize`(): kotlin.ULong? - - suspend fun `openBi`(): BiStream - - suspend fun `openUni`(): SendStream - - suspend fun `readDatagram`(): kotlin.ByteArray - - fun `remoteAddress`(): kotlin.String - - fun `rtt`(): kotlin.ULong - - fun `sendDatagram`(`data`: kotlin.ByteArray) - - suspend fun `sendDatagramWait`(`data`: kotlin.ByteArray) - - fun `setMaxConcurrentBiiStream`(`count`: kotlin.ULong) - - fun `setMaxConcurrentUniStream`(`count`: kotlin.ULong) - - fun `setReceiveWindow`(`count`: kotlin.ULong) - - fun `stableId`(): kotlin.ULong - - companion object -} - -open class Connection : - Disposable, - AutoCloseable, - ConnectionInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_connection(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_connection(pointer!!, status) - } - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `acceptBi`(): BiStream = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_accept_bi( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeBiStream.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `acceptUni`(): RecvStream = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_accept_uni( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeRecvStream.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - override fun `close`( - `errorCode`: kotlin.ULong, - `reason`: kotlin.ByteArray, - ) = callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_close( - it, - FfiConverterULong.lower(`errorCode`), - FfiConverterByteArray.lower(`reason`), - _status, - ) - } - } - - override fun `closeReason`(): kotlin.String? = - FfiConverterOptionalString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_close_reason( - it, - _status, - ) - } - }, - ) - - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `closed`(): kotlin.String = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_closed( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterString.lift(it) }, - // Error FFI converter - UniffiNullRustCallStatusErrorHandler, - ) - - override fun `datagramSendBufferSpace`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_datagram_send_buffer_space( - it, - _status, - ) - } - }, - ) - - @Throws(IrohException::class) - override fun `getRemoteNodeId`(): PublicKey = - FfiConverterTypePublicKey.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_get_remote_node_id( - it, - _status, - ) - } - }, - ) - - override fun `localIp`(): kotlin.String? = - FfiConverterOptionalString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_local_ip( - it, - _status, - ) - } - }, - ) - - override fun `maxDatagramSize`(): kotlin.ULong? = - FfiConverterOptionalULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_max_datagram_size( - it, - _status, - ) - } - }, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `openBi`(): BiStream = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_open_bi( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeBiStream.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `openUni`(): SendStream = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_open_uni( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeSendStream.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `readDatagram`(): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_read_datagram( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - override fun `remoteAddress`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_remote_address( - it, - _status, - ) - } - }, - ) - - override fun `rtt`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_rtt( - it, - _status, - ) - } - }, - ) - - @Throws(IrohException::class) - override fun `sendDatagram`(`data`: kotlin.ByteArray) = - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_send_datagram( - it, - FfiConverterByteArray.lower(`data`), - _status, - ) - } - } - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `sendDatagramWait`(`data`: kotlin.ByteArray) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_send_datagram_wait( - thisPtr, - FfiConverterByteArray.lower(`data`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - override fun `setMaxConcurrentBiiStream`(`count`: kotlin.ULong) = - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_set_max_concurrent_bii_stream( - it, - FfiConverterULong.lower(`count`), - _status, - ) - } - } - - @Throws(IrohException::class) - override fun `setMaxConcurrentUniStream`(`count`: kotlin.ULong) = - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_set_max_concurrent_uni_stream( - it, - FfiConverterULong.lower(`count`), - _status, - ) - } - } - - @Throws(IrohException::class) - override fun `setReceiveWindow`(`count`: kotlin.ULong) = - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_set_receive_window( - it, - FfiConverterULong.lower(`count`), - _status, - ) - } - } - - override fun `stableId`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connection_stable_id( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeConnection : FfiConverter { - override fun lower(value: Connection): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Connection = Connection(value) - - override fun read(buf: ByteBuffer): Connection { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Connection) = 8UL - - override fun write( - value: Connection, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The type of connection we have to the node - */ -public interface ConnectionTypeInterface { - /** - * Return the socket address if this is a direct connection - */ - fun `asDirect`(): kotlin.String - - /** - * Return the socket address and DERP url if this is a mixed connection - */ - fun `asMixed`(): ConnectionTypeMixed - - /** - * Return the derp url if this is a relay connection - */ - fun `asRelay`(): kotlin.String - - /** - * Whether connection is direct, relay, mixed, or none - */ - fun `type`(): ConnType - - companion object -} - -/** - * The type of connection we have to the node - */ -open class ConnectionType : - Disposable, - AutoCloseable, - ConnectionTypeInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_connectiontype(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_connectiontype(pointer!!, status) - } - - /** - * Return the socket address if this is a direct connection - */ - override fun `asDirect`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connectiontype_as_direct( - it, - _status, - ) - } - }, - ) - - /** - * Return the socket address and DERP url if this is a mixed connection - */ - override fun `asMixed`(): ConnectionTypeMixed = - FfiConverterTypeConnectionTypeMixed.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connectiontype_as_mixed( - it, - _status, - ) - } - }, - ) - - /** - * Return the derp url if this is a relay connection - */ - override fun `asRelay`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connectiontype_as_relay( - it, - _status, - ) - } - }, - ) - - /** - * Whether connection is direct, relay, mixed, or none - */ - override fun `type`(): ConnType = - FfiConverterTypeConnType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_connectiontype_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeConnectionType : FfiConverter { - override fun lower(value: ConnectionType): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): ConnectionType = ConnectionType(value) - - override fun read(buf: ByteBuffer): ConnectionType { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: ConnectionType) = 8UL - - override fun write( - value: ConnectionType, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Information about a direct address. - */ -public interface DirectAddrInfoInterface { - /** - * Get the reported address - */ - fun `addr`(): kotlin.String - - /** - * Get the last control message received by this node - */ - fun `lastControl`(): LatencyAndControlMsg? - - /** - * Get how long ago the last payload message was received for this node - */ - fun `lastPayload`(): java.time.Duration? - - /** - * Get the reported latency, if it exists - */ - fun `latency`(): java.time.Duration? - - companion object -} - -/** - * Information about a direct address. - */ -open class DirectAddrInfo : - Disposable, - AutoCloseable, - DirectAddrInfoInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_directaddrinfo(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_directaddrinfo(pointer!!, status) - } - - /** - * Get the reported address - */ - override fun `addr`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_directaddrinfo_addr( - it, - _status, - ) - } - }, - ) - - /** - * Get the last control message received by this node - */ - override fun `lastControl`(): LatencyAndControlMsg? = - FfiConverterOptionalTypeLatencyAndControlMsg.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_directaddrinfo_last_control( - it, - _status, - ) - } - }, - ) - - /** - * Get how long ago the last payload message was received for this node - */ - override fun `lastPayload`(): java.time.Duration? = - FfiConverterOptionalDuration.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_directaddrinfo_last_payload( - it, - _status, - ) - } - }, - ) - - /** - * Get the reported latency, if it exists - */ - override fun `latency`(): java.time.Duration? = - FfiConverterOptionalDuration.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_directaddrinfo_latency( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDirectAddrInfo : FfiConverter { - override fun lower(value: DirectAddrInfo): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): DirectAddrInfo = DirectAddrInfo(value) - - override fun read(buf: ByteBuffer): DirectAddrInfo { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DirectAddrInfo) = 8UL - - override fun write( - value: DirectAddrInfo, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A representation of a mutable, synchronizable key-value store. - */ -public interface DocInterface { - /** - * Close the document. - */ - suspend fun `closeMe`() - - /** - * Delete entries that match the given `author` and key `prefix`. - * - * This inserts an empty entry with the key set to `prefix`, effectively clearing all other - * entries whose key starts with or is equal to the given `prefix`. - * - * Returns the number of entries deleted. - */ - suspend fun `delete`( - `authorId`: AuthorId, - `prefix`: kotlin.ByteArray, - ): kotlin.ULong - - /** - * Export an entry as a file to a given absolute path - */ - suspend fun `exportFile`( - `entry`: Entry, - `path`: kotlin.String, - `cb`: DocExportFileCallback?, - ) - - /** - * Get the download policy for this document - */ - suspend fun `getDownloadPolicy`(): DownloadPolicy - - /** - * Get an entry for a key and author. - */ - suspend fun `getExact`( - `author`: AuthorId, - `key`: kotlin.ByteArray, - `includeEmpty`: kotlin.Boolean, - ): Entry? - - /** - * Get entries. - * - * Note: this allocates for each `Entry`, if you have many `Entry`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - suspend fun `getMany`(`query`: Query): List - - /** - * Get the latest entry for a key and author. - */ - suspend fun `getOne`(`query`: Query): Entry? - - /** - * Get sync peers for this document - */ - suspend fun `getSyncPeers`(): List? - - /** - * Get the document id of this doc. - */ - fun `id`(): kotlin.String - - /** - * Add an entry from an absolute file path - */ - suspend fun `importFile`( - `author`: AuthorId, - `key`: kotlin.ByteArray, - `path`: kotlin.String, - `inPlace`: kotlin.Boolean, - `cb`: DocImportFileCallback?, - ) - - /** - * Stop the live sync for this document. - */ - suspend fun `leave`() - - /** - * Set the content of a key to a byte array. - */ - suspend fun `setBytes`( - `authorId`: AuthorId, - `key`: kotlin.ByteArray, - `value`: kotlin.ByteArray, - ): Hash - - /** - * Set the download policy for this document - */ - suspend fun `setDownloadPolicy`(`policy`: DownloadPolicy) - - /** - * Set an entries on the doc via its key, hash, and size. - */ - suspend fun `setHash`( - `authorId`: AuthorId, - `key`: kotlin.ByteArray, - `hash`: Hash, - `size`: kotlin.ULong, - ) - - /** - * Share this document with peers over a ticket. - */ - suspend fun `share`( - `mode`: ShareMode, - `addrOptions`: AddrInfoOptions, - ): DocTicket - - /** - * Start to sync this document with a list of peers. - */ - suspend fun `startSync`(`peers`: List) - - /** - * Get status info for this document - */ - suspend fun `status`(): OpenState - - /** - * Subscribe to events for this document. - */ - suspend fun `subscribe`(`cb`: SubscribeCallback) - - companion object -} - -/** - * A representation of a mutable, synchronizable key-value store. - */ -open class Doc : - Disposable, - AutoCloseable, - DocInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_doc(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_doc(pointer!!, status) - } - - /** - * Close the document. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `closeMe`() = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_close_me( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Delete entries that match the given `author` and key `prefix`. - * - * This inserts an empty entry with the key set to `prefix`, effectively clearing all other - * entries whose key starts with or is equal to the given `prefix`. - * - * Returns the number of entries deleted. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `delete`( - `authorId`: AuthorId, - `prefix`: kotlin.ByteArray, - ): kotlin.ULong = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_delete( - thisPtr, - FfiConverterTypeAuthorId.lower(`authorId`), - FfiConverterByteArray.lower(`prefix`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterULong.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Export an entry as a file to a given absolute path - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `exportFile`( - `entry`: Entry, - `path`: kotlin.String, - `cb`: DocExportFileCallback?, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_export_file( - thisPtr, - FfiConverterTypeEntry.lower(`entry`), - FfiConverterString.lower(`path`), - FfiConverterOptionalTypeDocExportFileCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get the download policy for this document - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getDownloadPolicy`(): DownloadPolicy = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_get_download_policy( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeDownloadPolicy.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get an entry for a key and author. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getExact`( - `author`: AuthorId, - `key`: kotlin.ByteArray, - `includeEmpty`: kotlin.Boolean, - ): Entry? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_get_exact( - thisPtr, - FfiConverterTypeAuthorId.lower(`author`), - FfiConverterByteArray.lower(`key`), - FfiConverterBoolean.lower(`includeEmpty`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeEntry.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get entries. - * - * Note: this allocates for each `Entry`, if you have many `Entry`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getMany`(`query`: Query): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_get_many( - thisPtr, - FfiConverterTypeQuery.lower(`query`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeEntry.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get the latest entry for a key and author. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getOne`(`query`: Query): Entry? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_get_one( - thisPtr, - FfiConverterTypeQuery.lower(`query`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeEntry.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get sync peers for this document - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `getSyncPeers`(): List? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_get_sync_peers( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalSequenceByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get the document id of this doc. - */ - override fun `id`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_id( - it, - _status, - ) - } - }, - ) - - /** - * Add an entry from an absolute file path - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `importFile`( - `author`: AuthorId, - `key`: kotlin.ByteArray, - `path`: kotlin.String, - `inPlace`: kotlin.Boolean, - `cb`: DocImportFileCallback?, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_import_file( - thisPtr, - FfiConverterTypeAuthorId.lower(`author`), - FfiConverterByteArray.lower(`key`), - FfiConverterString.lower(`path`), - FfiConverterBoolean.lower(`inPlace`), - FfiConverterOptionalTypeDocImportFileCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Stop the live sync for this document. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `leave`() = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_leave( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Set the content of a key to a byte array. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `setBytes`( - `authorId`: AuthorId, - `key`: kotlin.ByteArray, - `value`: kotlin.ByteArray, - ): Hash = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_set_bytes( - thisPtr, - FfiConverterTypeAuthorId.lower(`authorId`), - FfiConverterByteArray.lower(`key`), - FfiConverterByteArray.lower(`value`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeHash.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Set the download policy for this document - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `setDownloadPolicy`(`policy`: DownloadPolicy) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_set_download_policy( - thisPtr, - FfiConverterTypeDownloadPolicy.lower(`policy`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Set an entries on the doc via its key, hash, and size. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `setHash`( - `authorId`: AuthorId, - `key`: kotlin.ByteArray, - `hash`: Hash, - `size`: kotlin.ULong, - ) = uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_set_hash( - thisPtr, - FfiConverterTypeAuthorId.lower(`authorId`), - FfiConverterByteArray.lower(`key`), - FfiConverterTypeHash.lower(`hash`), - FfiConverterULong.lower(`size`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Share this document with peers over a ticket. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `share`( - `mode`: ShareMode, - `addrOptions`: AddrInfoOptions, - ): DocTicket = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_share( - thisPtr, - FfiConverterTypeShareMode.lower(`mode`), - FfiConverterTypeAddrInfoOptions.lower(`addrOptions`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeDocTicket.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Start to sync this document with a list of peers. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `startSync`(`peers`: List) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_start_sync( - thisPtr, - FfiConverterSequenceTypeNodeAddr.lower(`peers`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get status info for this document - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `status`(): OpenState = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_status( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterTypeOpenState.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Subscribe to events for this document. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `subscribe`(`cb`: SubscribeCallback) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_doc_subscribe( - thisPtr, - FfiConverterTypeSubscribeCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDoc : FfiConverter { - override fun lower(value: Doc): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Doc = Doc(value) - - override fun read(buf: ByteBuffer): Doc { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Doc) = 8UL - - override fun write( - value: Doc, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The `progress` method will be called for each `DocExportProgress` event that is - * emitted during a `doc.export_file()` call. Use the `DocExportProgress.type()` - * method to check the `DocExportProgressType` - */ -public interface DocExportFileCallback { - suspend fun `progress`(`progress`: DocExportProgress) - - companion object -} - -/** - * The `progress` method will be called for each `DocExportProgress` event that is - * emitted during a `doc.export_file()` call. Use the `DocExportProgress.type()` - * method to check the `DocExportProgressType` - */ -open class DocExportFileCallbackImpl : - Disposable, - AutoCloseable, - DocExportFileCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_docexportfilecallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_docexportfilecallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `progress`(`progress`: DocExportProgress) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docexportfilecallback_progress( - thisPtr, - FfiConverterTypeDocExportProgress.lower(`progress`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceDocExportFileCallback { - internal object `progress` : UniffiCallbackInterfaceDocExportFileCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeDocExportFileCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`progress`( - FfiConverterTypeDocExportProgress.lift(`progress`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeDocExportFileCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceDocExportFileCallback.UniffiByValue( - `progress`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_docexportfilecallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportFileCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: DocExportFileCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): DocExportFileCallback = DocExportFileCallbackImpl(value) - - override fun read(buf: ByteBuffer): DocExportFileCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DocExportFileCallback) = 8UL - - override fun write( - value: DocExportFileCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Progress updates for the doc import file operation. - */ -public interface DocExportProgressInterface { - /** - * Return the `DocExportProgressAbort` - */ - fun `asAbort`(): DocExportProgressAbort - - /** - * Return the `DocExportProgressFound` event - */ - fun `asFound`(): DocExportProgressFound - - /** - * Return the `DocExportProgressProgress` event - */ - fun `asProgress`(): DocExportProgressProgress - - /** - * Get the type of event - */ - fun `type`(): DocExportProgressType - - companion object -} - -/** - * Progress updates for the doc import file operation. - */ -open class DocExportProgress : - Disposable, - AutoCloseable, - DocExportProgressInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_docexportprogress(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_docexportprogress(pointer!!, status) - } - - /** - * Return the `DocExportProgressAbort` - */ - override fun `asAbort`(): DocExportProgressAbort = - FfiConverterTypeDocExportProgressAbort.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docexportprogress_as_abort( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DocExportProgressFound` event - */ - override fun `asFound`(): DocExportProgressFound = - FfiConverterTypeDocExportProgressFound.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docexportprogress_as_found( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DocExportProgressProgress` event - */ - override fun `asProgress`(): DocExportProgressProgress = - FfiConverterTypeDocExportProgressProgress.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docexportprogress_as_progress( - it, - _status, - ) - } - }, - ) - - /** - * Get the type of event - */ - override fun `type`(): DocExportProgressType = - FfiConverterTypeDocExportProgressType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docexportprogress_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportProgress : FfiConverter { - override fun lower(value: DocExportProgress): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): DocExportProgress = DocExportProgress(value) - - override fun read(buf: ByteBuffer): DocExportProgress { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DocExportProgress) = 8UL - - override fun write( - value: DocExportProgress, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The `progress` method will be called for each `DocImportProgress` event that is - * emitted during a `doc.import_file()` call. Use the `DocImportProgress.type()` - * method to check the `DocImportProgressType` - */ -public interface DocImportFileCallback { - suspend fun `progress`(`progress`: DocImportProgress) - - companion object -} - -/** - * The `progress` method will be called for each `DocImportProgress` event that is - * emitted during a `doc.import_file()` call. Use the `DocImportProgress.type()` - * method to check the `DocImportProgressType` - */ -open class DocImportFileCallbackImpl : - Disposable, - AutoCloseable, - DocImportFileCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_docimportfilecallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_docimportfilecallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `progress`(`progress`: DocImportProgress) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportfilecallback_progress( - thisPtr, - FfiConverterTypeDocImportProgress.lower(`progress`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceDocImportFileCallback { - internal object `progress` : UniffiCallbackInterfaceDocImportFileCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeDocImportFileCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`progress`( - FfiConverterTypeDocImportProgress.lift(`progress`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeDocImportFileCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceDocImportFileCallback.UniffiByValue( - `progress`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_docimportfilecallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportFileCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: DocImportFileCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): DocImportFileCallback = DocImportFileCallbackImpl(value) - - override fun read(buf: ByteBuffer): DocImportFileCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DocImportFileCallback) = 8UL - - override fun write( - value: DocImportFileCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Progress updates for the doc import file operation. - */ -public interface DocImportProgressInterface { - /** - * Return the `DocImportProgressAbort` - */ - fun `asAbort`(): DocImportProgressAbort - - /** - * Return the `DocImportProgressAllDone` - */ - fun `asAllDone`(): DocImportProgressAllDone - - /** - * Return the `DocImportProgressFound` event - */ - fun `asFound`(): DocImportProgressFound - - /** - * Return the `DocImportProgressDone` event - */ - fun `asIngestDone`(): DocImportProgressIngestDone - - /** - * Return the `DocImportProgressProgress` event - */ - fun `asProgress`(): DocImportProgressProgress - - /** - * Get the type of event - */ - fun `type`(): DocImportProgressType - - companion object -} - -/** - * Progress updates for the doc import file operation. - */ -open class DocImportProgress : - Disposable, - AutoCloseable, - DocImportProgressInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_docimportprogress(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_docimportprogress(pointer!!, status) - } - - /** - * Return the `DocImportProgressAbort` - */ - override fun `asAbort`(): DocImportProgressAbort = - FfiConverterTypeDocImportProgressAbort.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportprogress_as_abort( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DocImportProgressAllDone` - */ - override fun `asAllDone`(): DocImportProgressAllDone = - FfiConverterTypeDocImportProgressAllDone.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportprogress_as_all_done( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DocImportProgressFound` event - */ - override fun `asFound`(): DocImportProgressFound = - FfiConverterTypeDocImportProgressFound.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportprogress_as_found( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DocImportProgressDone` event - */ - override fun `asIngestDone`(): DocImportProgressIngestDone = - FfiConverterTypeDocImportProgressIngestDone.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportprogress_as_ingest_done( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DocImportProgressProgress` event - */ - override fun `asProgress`(): DocImportProgressProgress = - FfiConverterTypeDocImportProgressProgress.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportprogress_as_progress( - it, - _status, - ) - } - }, - ) - - /** - * Get the type of event - */ - override fun `type`(): DocImportProgressType = - FfiConverterTypeDocImportProgressType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docimportprogress_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgress : FfiConverter { - override fun lower(value: DocImportProgress): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): DocImportProgress = DocImportProgress(value) - - override fun read(buf: ByteBuffer): DocImportProgress { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DocImportProgress) = 8UL - - override fun write( - value: DocImportProgress, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Contains both a key (either secret or public) to a document, and a list of peers to join. - */ -public interface DocTicketInterface { - companion object -} - -/** - * Contains both a key (either secret or public) to a document, and a list of peers to join. - */ -open class DocTicket : - Disposable, - AutoCloseable, - DocTicketInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - constructor(`str`: kotlin.String) : - this( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_docticket_new( - FfiConverterString.lower(`str`), - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_docticket(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_docticket(pointer!!, status) - } - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docticket_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocTicket : FfiConverter { - override fun lower(value: DocTicket): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): DocTicket = DocTicket(value) - - override fun read(buf: ByteBuffer): DocTicket { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DocTicket) = 8UL - - override fun write( - value: DocTicket, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh docs client. - */ -public interface DocsInterface { - /** - * Create a new doc. - */ - suspend fun `create`(): Doc - - /** - * Delete a document from the local node. - * - * This is a destructive operation. Both the document secret key and all entries in the - * document will be permanently deleted from the node's storage. Content blobs will be deleted - * through garbage collection unless they are referenced from another document or tag. - */ - suspend fun `dropDoc`(`docId`: kotlin.String) - - /** - * Join and sync with an already existing document. - */ - suspend fun `join`(`ticket`: DocTicket): Doc - - /** - * Join and sync with an already existing document and subscribe to events on that document. - */ - suspend fun `joinAndSubscribe`( - `ticket`: DocTicket, - `cb`: SubscribeCallback, - ): Doc - - /** - * List all the docs we have access to on this node. - */ - suspend fun `list`(): List - - /** - * Get a [`Doc`]. - * - * Returns None if the document cannot be found. - */ - suspend fun `open`(`id`: kotlin.String): Doc? - - companion object -} - -/** - * Iroh docs client. - */ -open class Docs : - Disposable, - AutoCloseable, - DocsInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_docs(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_docs(pointer!!, status) - } - - /** - * Create a new doc. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `create`(): Doc = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docs_create( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeDoc.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Delete a document from the local node. - * - * This is a destructive operation. Both the document secret key and all entries in the - * document will be permanently deleted from the node's storage. Content blobs will be deleted - * through garbage collection unless they are referenced from another document or tag. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `dropDoc`(`docId`: kotlin.String) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docs_drop_doc( - thisPtr, - FfiConverterString.lower(`docId`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Join and sync with an already existing document. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `join`(`ticket`: DocTicket): Doc = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docs_join( - thisPtr, - FfiConverterTypeDocTicket.lower(`ticket`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeDoc.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Join and sync with an already existing document and subscribe to events on that document. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `joinAndSubscribe`( - `ticket`: DocTicket, - `cb`: SubscribeCallback, - ): Doc = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docs_join_and_subscribe( - thisPtr, - FfiConverterTypeDocTicket.lower(`ticket`), - FfiConverterTypeSubscribeCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeDoc.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * List all the docs we have access to on this node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `list`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docs_list( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeNamespaceAndCapability.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get a [`Doc`]. - * - * Returns None if the document cannot be found. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `open`(`id`: kotlin.String): Doc? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_docs_open( - thisPtr, - FfiConverterString.lower(`id`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeDoc.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocs : FfiConverter { - override fun lower(value: Docs): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Docs = Docs(value) - - override fun read(buf: ByteBuffer): Docs { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Docs) = 8UL - - override fun write( - value: Docs, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The `progress` method will be called for each `DownloadProgress` event that is emitted during - * a `node.blobs_download`. Use the `DownloadProgress.type()` method to check the - * `DownloadProgressType` of the event. - */ -public interface DownloadCallback { - suspend fun `progress`(`progress`: DownloadProgress) - - companion object -} - -/** - * The `progress` method will be called for each `DownloadProgress` event that is emitted during - * a `node.blobs_download`. Use the `DownloadProgress.type()` method to check the - * `DownloadProgressType` of the event. - */ -open class DownloadCallbackImpl : - Disposable, - AutoCloseable, - DownloadCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_downloadcallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_downloadcallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `progress`(`progress`: DownloadProgress) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadcallback_progress( - thisPtr, - FfiConverterTypeDownloadProgress.lower(`progress`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceDownloadCallback { - internal object `progress` : UniffiCallbackInterfaceDownloadCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `progress`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeDownloadCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`progress`( - FfiConverterTypeDownloadProgress.lift(`progress`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeDownloadCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceDownloadCallback.UniffiByValue( - `progress`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_downloadcallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: DownloadCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): DownloadCallback = DownloadCallbackImpl(value) - - override fun read(buf: ByteBuffer): DownloadCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DownloadCallback) = 8UL - - override fun write( - value: DownloadCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Download policy to decide which content blobs shall be downloaded. - */ -public interface DownloadPolicyInterface { - companion object -} - -/** - * Download policy to decide which content blobs shall be downloaded. - */ -open class DownloadPolicy : - Disposable, - AutoCloseable, - DownloadPolicyInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_downloadpolicy(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_downloadpolicy(pointer!!, status) - } - - companion object { - /** - * Download everything - */ - fun `everything`(): DownloadPolicy = - FfiConverterTypeDownloadPolicy.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_downloadpolicy_everything( - _status, - ) - }, - ) - - /** - * Download everything except keys that match the given filters - */ - fun `everythingExcept`(`filters`: List): DownloadPolicy = - FfiConverterTypeDownloadPolicy.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_downloadpolicy_everything_except( - FfiConverterSequenceTypeFilterKind.lower(`filters`), - _status, - ) - }, - ) - - /** - * Download nothing - */ - fun `nothing`(): DownloadPolicy = - FfiConverterTypeDownloadPolicy.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_downloadpolicy_nothing( - _status, - ) - }, - ) - - /** - * Download nothing except keys that match the given filters - */ - fun `nothingExcept`(`filters`: List): DownloadPolicy = - FfiConverterTypeDownloadPolicy.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_downloadpolicy_nothing_except( - FfiConverterSequenceTypeFilterKind.lower(`filters`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadPolicy : FfiConverter { - override fun lower(value: DownloadPolicy): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): DownloadPolicy = DownloadPolicy(value) - - override fun read(buf: ByteBuffer): DownloadPolicy { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DownloadPolicy) = 8UL - - override fun write( - value: DownloadPolicy, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Progress updates for the get operation. - */ -public interface DownloadProgressInterface { - /** - * Return the `DownloadProgressAbort` event - */ - fun `asAbort`(): DownloadProgressAbort - - /** - * Return the `DownloadProgressAllDone` event - */ - fun `asAllDone`(): DownloadProgressAllDone - - /** - * Return the `DownloadProgressDone` event - */ - fun `asDone`(): DownloadProgressDone - - /** - * Return the `DownloadProgressFound` event - */ - fun `asFound`(): DownloadProgressFound - - /** - * Return the `DownloadProgressFoundHashSeq` event - */ - fun `asFoundHashSeq`(): DownloadProgressFoundHashSeq - - /** - * Return the `DownloadProgressFoundLocal` event - */ - fun `asFoundLocal`(): DownloadProgressFoundLocal - - /** - * Return the `DownloadProgressProgress` event - */ - fun `asProgress`(): DownloadProgressProgress - - /** - * Get the type of event - * note that there is no `as_connected` method, as the `Connected` event has no associated data - */ - fun `type`(): DownloadProgressType - - companion object -} - -/** - * Progress updates for the get operation. - */ -open class DownloadProgress : - Disposable, - AutoCloseable, - DownloadProgressInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_downloadprogress(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_downloadprogress(pointer!!, status) - } - - /** - * Return the `DownloadProgressAbort` event - */ - override fun `asAbort`(): DownloadProgressAbort = - FfiConverterTypeDownloadProgressAbort.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_abort( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DownloadProgressAllDone` event - */ - override fun `asAllDone`(): DownloadProgressAllDone = - FfiConverterTypeDownloadProgressAllDone.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_all_done( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DownloadProgressDone` event - */ - override fun `asDone`(): DownloadProgressDone = - FfiConverterTypeDownloadProgressDone.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_done( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DownloadProgressFound` event - */ - override fun `asFound`(): DownloadProgressFound = - FfiConverterTypeDownloadProgressFound.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_found( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DownloadProgressFoundHashSeq` event - */ - override fun `asFoundHashSeq`(): DownloadProgressFoundHashSeq = - FfiConverterTypeDownloadProgressFoundHashSeq.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_found_hash_seq( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DownloadProgressFoundLocal` event - */ - override fun `asFoundLocal`(): DownloadProgressFoundLocal = - FfiConverterTypeDownloadProgressFoundLocal.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_found_local( - it, - _status, - ) - } - }, - ) - - /** - * Return the `DownloadProgressProgress` event - */ - override fun `asProgress`(): DownloadProgressProgress = - FfiConverterTypeDownloadProgressProgress.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_as_progress( - it, - _status, - ) - } - }, - ) - - /** - * Get the type of event - * note that there is no `as_connected` method, as the `Connected` event has no associated data - */ - override fun `type`(): DownloadProgressType = - FfiConverterTypeDownloadProgressType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_downloadprogress_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgress : FfiConverter { - override fun lower(value: DownloadProgress): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): DownloadProgress = DownloadProgress(value) - - override fun read(buf: ByteBuffer): DownloadProgress { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: DownloadProgress) = 8UL - - override fun write( - value: DownloadProgress, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface EndpointInterface { - suspend fun `connect`( - `nodeAddr`: NodeAddr, - `alpn`: kotlin.ByteArray, - ): Connection - - /** - * The string representation of this endpoint's NodeId. - */ - fun `nodeId`(): kotlin.String - - companion object -} - -open class Endpoint : - Disposable, - AutoCloseable, - EndpointInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_endpoint(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_endpoint(pointer!!, status) - } - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `connect`( - `nodeAddr`: NodeAddr, - `alpn`: kotlin.ByteArray, - ): Connection = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_endpoint_connect( - thisPtr, - FfiConverterTypeNodeAddr.lower(`nodeAddr`), - FfiConverterByteArray.lower(`alpn`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeConnection.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * The string representation of this endpoint's NodeId. - */ - @Throws(IrohException::class) - override fun `nodeId`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_endpoint_node_id( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeEndpoint : FfiConverter { - override fun lower(value: Endpoint): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Endpoint = Endpoint(value) - - override fun read(buf: ByteBuffer): Endpoint { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Endpoint) = 8UL - - override fun write( - value: Endpoint, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A single entry in a [`Doc`] - * - * An entry is identified by a key, its [`AuthorId`], and the [`Doc`]'s - * namespace id. Its value is the 32-byte BLAKE3 [`hash`] - * of the entry's content data, the size of this content data, and a timestamp. - */ -public interface EntryInterface { - /** - * Get the [`AuthorId`] of this entry. - */ - fun `author`(): AuthorId - - /** - * Get the content_hash of this entry. - */ - fun `contentHash`(): Hash - - /** - * Get the content_length of this entry. - */ - fun `contentLen`(): kotlin.ULong - - /** - * Get the key of this entry. - */ - fun `key`(): kotlin.ByteArray - - /** - * Get the namespace id of this entry. - */ - fun `namespace`(): kotlin.String - - /** - * Get the timestamp when this entry was written. - */ - fun `timestamp`(): kotlin.ULong - - companion object -} - -/** - * A single entry in a [`Doc`] - * - * An entry is identified by a key, its [`AuthorId`], and the [`Doc`]'s - * namespace id. Its value is the 32-byte BLAKE3 [`hash`] - * of the entry's content data, the size of this content data, and a timestamp. - */ -open class Entry : - Disposable, - AutoCloseable, - EntryInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_entry(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_entry(pointer!!, status) - } - - /** - * Get the [`AuthorId`] of this entry. - */ - override fun `author`(): AuthorId = - FfiConverterTypeAuthorId.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_entry_author( - it, - _status, - ) - } - }, - ) - - /** - * Get the content_hash of this entry. - */ - override fun `contentHash`(): Hash = - FfiConverterTypeHash.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_entry_content_hash( - it, - _status, - ) - } - }, - ) - - /** - * Get the content_length of this entry. - */ - override fun `contentLen`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_entry_content_len( - it, - _status, - ) - } - }, - ) - - /** - * Get the key of this entry. - */ - override fun `key`(): kotlin.ByteArray = - FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_entry_key( - it, - _status, - ) - } - }, - ) - - /** - * Get the namespace id of this entry. - */ - override fun `namespace`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_entry_namespace( - it, - _status, - ) - } - }, - ) - - /** - * Get the timestamp when this entry was written. - */ - override fun `timestamp`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_entry_timestamp( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeEntry : FfiConverter { - override fun lower(value: Entry): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Entry = Entry(value) - - override fun read(buf: ByteBuffer): Entry { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Entry) = 8UL - - override fun write( - value: Entry, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Filter strategy used in download policies. - */ -public interface FilterKindInterface { - /** - * Verifies whether this filter matches a given key - */ - fun `matches`(`key`: kotlin.ByteArray): kotlin.Boolean - - companion object -} - -/** - * Filter strategy used in download policies. - */ -open class FilterKind : - Disposable, - AutoCloseable, - FilterKindInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_filterkind(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_filterkind(pointer!!, status) - } - - /** - * Verifies whether this filter matches a given key - */ - override fun `matches`(`key`: kotlin.ByteArray): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_filterkind_matches( - it, - FfiConverterByteArray.lower(`key`), - _status, - ) - } - }, - ) - - companion object { - /** - * Returns a FilterKind that matches if the contained bytes and the key are the same. - */ - fun `exact`(`key`: kotlin.ByteArray): FilterKind = - FfiConverterTypeFilterKind.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_filterkind_exact( - FfiConverterByteArray.lower(`key`), - _status, - ) - }, - ) - - /** - * Returns a FilterKind that matches if the contained bytes are a prefix of the key. - */ - fun `prefix`(`prefix`: kotlin.ByteArray): FilterKind = - FfiConverterTypeFilterKind.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_filterkind_prefix( - FfiConverterByteArray.lower(`prefix`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeFilterKind : FfiConverter { - override fun lower(value: FilterKind): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): FilterKind = FilterKind(value) - - override fun read(buf: ByteBuffer): FilterKind { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: FilterKind) = 8UL - - override fun write( - value: FilterKind, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh gossip client. - */ -public interface GossipInterface { - suspend fun `subscribe`( - `topic`: kotlin.ByteArray, - `bootstrap`: List, - `cb`: GossipMessageCallback, - ): Sender - - companion object -} - -/** - * Iroh gossip client. - */ -open class Gossip : - Disposable, - AutoCloseable, - GossipInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_gossip(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_gossip(pointer!!, status) - } - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `subscribe`( - `topic`: kotlin.ByteArray, - `bootstrap`: List, - `cb`: GossipMessageCallback, - ): Sender = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_gossip_subscribe( - thisPtr, - FfiConverterByteArray.lower(`topic`), - FfiConverterSequenceString.lower(`bootstrap`), - FfiConverterTypeGossipMessageCallback.lower(`cb`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeSender.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeGossip : FfiConverter { - override fun lower(value: Gossip): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Gossip = Gossip(value) - - override fun read(buf: ByteBuffer): Gossip { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Gossip) = 8UL - - override fun write( - value: Gossip, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface GossipMessageCallback { - suspend fun `onMessage`(`msg`: Message) - - companion object -} - -open class GossipMessageCallbackImpl : - Disposable, - AutoCloseable, - GossipMessageCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_gossipmessagecallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_gossipmessagecallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `onMessage`(`msg`: Message) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_gossipmessagecallback_on_message( - thisPtr, - FfiConverterTypeMessage.lower(`msg`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceGossipMessageCallback { - internal object `onMessage` : UniffiCallbackInterfaceGossipMessageCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `msg`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeGossipMessageCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`onMessage`( - FfiConverterTypeMessage.lift(`msg`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeGossipMessageCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceGossipMessageCallback.UniffiByValue( - `onMessage`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_gossipmessagecallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeGossipMessageCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: GossipMessageCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): GossipMessageCallback = GossipMessageCallbackImpl(value) - - override fun read(buf: ByteBuffer): GossipMessageCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: GossipMessageCallback) = 8UL - - override fun write( - value: GossipMessageCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Hash type used throughout Iroh. A blake3 hash. - */ -public interface HashInterface { - /** - * Returns true if the Hash's have the same value - */ - fun `equal`(`other`: Hash): kotlin.Boolean - - /** - * Bytes of the hash. - */ - fun `toBytes`(): kotlin.ByteArray - - /** - * Convert the hash to a hex string. - */ - fun `toHex`(): kotlin.String - - companion object -} - -/** - * Hash type used throughout Iroh. A blake3 hash. - */ -open class Hash : - Disposable, - AutoCloseable, - HashInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * Calculate the hash of the provide bytes. - */ - constructor(`buf`: kotlin.ByteArray) : - this( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_hash_new( - FfiConverterByteArray.lower(`buf`), - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_hash(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_hash(pointer!!, status) - } - - /** - * Returns true if the Hash's have the same value - */ - override fun `equal`(`other`: Hash): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_hash_equal( - it, - FfiConverterTypeHash.lower(`other`), - _status, - ) - } - }, - ) - - /** - * Bytes of the hash. - */ - override fun `toBytes`(): kotlin.ByteArray = - FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_hash_to_bytes( - it, - _status, - ) - } - }, - ) - - /** - * Convert the hash to a hex string. - */ - override fun `toHex`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_hash_to_hex( - it, - _status, - ) - } - }, - ) - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_hash_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Create a `Hash` from its raw bytes representation. - */ - @Throws(IrohException::class) - fun `fromBytes`(`bytes`: kotlin.ByteArray): Hash = - FfiConverterTypeHash.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_hash_from_bytes( - FfiConverterByteArray.lower(`bytes`), - _status, - ) - }, - ) - - /** - * Make a Hash from hex string - */ - @Throws(IrohException::class) - fun `fromString`(`s`: kotlin.String): Hash = - FfiConverterTypeHash.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_hash_from_string( - FfiConverterString.lower(`s`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeHash : FfiConverter { - override fun lower(value: Hash): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Hash = Hash(value) - - override fun read(buf: ByteBuffer): Hash { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Hash) = 8UL - - override fun write( - value: Hash, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * An Iroh node. Allows you to sync, store, and transfer data. - */ -public interface IrohInterface { - /** - * Access to gossip specific funtionaliy. - */ - fun `authors`(): Authors - - /** - * Access to blob specific funtionaliy. - */ - fun `blobs`(): Blobs - - /** - * Access to docs specific funtionaliy. - */ - fun `docs`(): Docs - - /** - * Access to gossip specific funtionaliy. - */ - fun `gossip`(): Gossip - - /** - * Access to blob specific funtionaliy. - */ - fun `net`(): Net - - /** - * Access to node specific funtionaliy. - */ - fun `node`(): Node - - /** - * Access to tags specific funtionaliy. - */ - fun `tags`(): Tags - - companion object -} - -/** - * An Iroh node. Allows you to sync, store, and transfer data. - */ -open class Iroh : - Disposable, - AutoCloseable, - IrohInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_iroh(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_iroh(pointer!!, status) - } - - /** - * Access to gossip specific funtionaliy. - */ - override fun `authors`(): Authors = - FfiConverterTypeAuthors.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_authors( - it, - _status, - ) - } - }, - ) - - /** - * Access to blob specific funtionaliy. - */ - override fun `blobs`(): Blobs = - FfiConverterTypeBlobs.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_blobs( - it, - _status, - ) - } - }, - ) - - /** - * Access to docs specific funtionaliy. - */ - override fun `docs`(): Docs = - FfiConverterTypeDocs.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_docs( - it, - _status, - ) - } - }, - ) - - /** - * Access to gossip specific funtionaliy. - */ - override fun `gossip`(): Gossip = - FfiConverterTypeGossip.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_gossip( - it, - _status, - ) - } - }, - ) - - /** - * Access to blob specific funtionaliy. - */ - override fun `net`(): Net = - FfiConverterTypeNet.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_net( - it, - _status, - ) - } - }, - ) - - /** - * Access to node specific funtionaliy. - */ - override fun `node`(): Node = - FfiConverterTypeNode.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_node( - it, - _status, - ) - } - }, - ) - - /** - * Access to tags specific funtionaliy. - */ - override fun `tags`(): Tags = - FfiConverterTypeTags.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroh_tags( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Create a new iroh node. - * - * All data will be only persistet in memory. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - suspend fun `memory`(): Iroh = - uniffiRustCallAsync( - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_iroh_memory(), - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeIroh.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Create a new in memory iroh node with options. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - suspend fun `memoryWithOptions`(`options`: NodeOptions): Iroh = - uniffiRustCallAsync( - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_iroh_memory_with_options(FfiConverterTypeNodeOptions.lower(`options`)), - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeIroh.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Create a new iroh node. - * - * The `path` param should be a directory where we can store or load - * iroh data from a previous session. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - suspend fun `persistent`(`path`: kotlin.String): Iroh = - uniffiRustCallAsync( - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_iroh_persistent(FfiConverterString.lower(`path`)), - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeIroh.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Create a new iroh node with options. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - suspend fun `persistentWithOptions`( - `path`: kotlin.String, - `options`: NodeOptions, - ): Iroh = - uniffiRustCallAsync( - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_iroh_persistent_with_options( - FfiConverterString.lower(`path`), - FfiConverterTypeNodeOptions.lower(`options`), - ), - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeIroh.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeIroh : FfiConverter { - override fun lower(value: Iroh): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Iroh = Iroh(value) - - override fun read(buf: ByteBuffer): Iroh { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Iroh) = 8UL - - override fun write( - value: Iroh, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * An Error. - */ -public interface IrohExceptionInterface { - fun `message`(): kotlin.String - - companion object -} - -/** - * An Error. - */ - -open class IrohException : - kotlin.Exception, - Disposable, - AutoCloseable, - IrohExceptionInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_iroherror(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_iroherror(pointer!!, status) - } - - override fun `message`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_iroherror_message( - it, - _status, - ) - } - }, - ) - - companion object ErrorHandler : UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): IrohException { - // Due to some mismatches in the ffi converter mechanisms, errors are a RustBuffer. - val bb = error_buf.asByteBuffer() - if (bb == null) { - throw InternalException("?") - } - return FfiConverterTypeIrohError.read(bb) - } - } -} - -/** - * @suppress - */ -public object FfiConverterTypeIrohError : FfiConverter { - override fun lower(value: IrohException): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): IrohException = IrohException(value) - - override fun read(buf: ByteBuffer): IrohException { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: IrohException) = 8UL - - override fun write( - value: IrohException, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Events informing about actions of the live sync progress - */ -public interface LiveEventInterface { - /** - * For `LiveEventType::ContentReady`, returns a Hash - */ - fun `asContentReady`(): Hash - - /** - * For `LiveEventType::InsertLocal`, returns an Entry - */ - fun `asInsertLocal`(): Entry - - /** - * For `LiveEventType::InsertRemote`, returns an InsertRemoteEvent - */ - fun `asInsertRemote`(): InsertRemoteEvent - - /** - * For `LiveEventType::NeighborDown`, returns a PublicKey - */ - fun `asNeighborDown`(): PublicKey - - /** - * For `LiveEventType::NeighborUp`, returns a PublicKey - */ - fun `asNeighborUp`(): PublicKey - - /** - * For `LiveEventType::SyncFinished`, returns a SyncEvent - */ - fun `asSyncFinished`(): SyncEvent - - /** - * The type LiveEvent - */ - fun `type`(): LiveEventType - - companion object -} - -/** - * Events informing about actions of the live sync progress - */ -open class LiveEvent : - Disposable, - AutoCloseable, - LiveEventInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_liveevent(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_liveevent(pointer!!, status) - } - - /** - * For `LiveEventType::ContentReady`, returns a Hash - */ - override fun `asContentReady`(): Hash = - FfiConverterTypeHash.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_as_content_ready( - it, - _status, - ) - } - }, - ) - - /** - * For `LiveEventType::InsertLocal`, returns an Entry - */ - override fun `asInsertLocal`(): Entry = - FfiConverterTypeEntry.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_as_insert_local( - it, - _status, - ) - } - }, - ) - - /** - * For `LiveEventType::InsertRemote`, returns an InsertRemoteEvent - */ - override fun `asInsertRemote`(): InsertRemoteEvent = - FfiConverterTypeInsertRemoteEvent.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_as_insert_remote( - it, - _status, - ) - } - }, - ) - - /** - * For `LiveEventType::NeighborDown`, returns a PublicKey - */ - override fun `asNeighborDown`(): PublicKey = - FfiConverterTypePublicKey.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_as_neighbor_down( - it, - _status, - ) - } - }, - ) - - /** - * For `LiveEventType::NeighborUp`, returns a PublicKey - */ - override fun `asNeighborUp`(): PublicKey = - FfiConverterTypePublicKey.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_as_neighbor_up( - it, - _status, - ) - } - }, - ) - - /** - * For `LiveEventType::SyncFinished`, returns a SyncEvent - */ - override fun `asSyncFinished`(): SyncEvent = - FfiConverterTypeSyncEvent.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_as_sync_finished( - it, - _status, - ) - } - }, - ) - - /** - * The type LiveEvent - */ - override fun `type`(): LiveEventType = - FfiConverterTypeLiveEventType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_liveevent_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeLiveEvent : FfiConverter { - override fun lower(value: LiveEvent): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): LiveEvent = LiveEvent(value) - - override fun read(buf: ByteBuffer): LiveEvent { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: LiveEvent) = 8UL - - override fun write( - value: LiveEvent, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Gossip message - */ -public interface MessageInterface { - fun `asError`(): kotlin.String - - fun `asJoined`(): List - - fun `asNeighborDown`(): kotlin.String - - fun `asNeighborUp`(): kotlin.String - - fun `asReceived`(): MessageContent - - fun `type`(): MessageType - - companion object -} - -/** - * Gossip message - */ -open class Message : - Disposable, - AutoCloseable, - MessageInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_message(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_message(pointer!!, status) - } - - override fun `asError`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_message_as_error( - it, - _status, - ) - } - }, - ) - - override fun `asJoined`(): List = - FfiConverterSequenceString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_message_as_joined( - it, - _status, - ) - } - }, - ) - - override fun `asNeighborDown`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_message_as_neighbor_down( - it, - _status, - ) - } - }, - ) - - override fun `asNeighborUp`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_message_as_neighbor_up( - it, - _status, - ) - } - }, - ) - - override fun `asReceived`(): MessageContent = - FfiConverterTypeMessageContent.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_message_as_received( - it, - _status, - ) - } - }, - ) - - override fun `type`(): MessageType = - FfiConverterTypeMessageType.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_message_type( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeMessage : FfiConverter { - override fun lower(value: Message): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Message = Message(value) - - override fun read(buf: ByteBuffer): Message { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Message) = 8UL - - override fun write( - value: Message, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh net client. - */ -public interface NetInterface { - /** - * Add a known node address to the node. - */ - suspend fun `addNodeAddr`(`addr`: NodeAddr) - - /** - * Get the relay server we are connected to. - */ - suspend fun `homeRelay`(): kotlin.String? - - /** - * Return the [`NodeAddr`] for this node. - */ - suspend fun `nodeAddr`(): NodeAddr - - /** - * The string representation of the PublicKey of this node. - */ - suspend fun `nodeId`(): kotlin.String - - /** - * Return connection information on the currently running node. - */ - suspend fun `remoteInfo`(`nodeId`: PublicKey): RemoteInfo? - - /** - * Return `ConnectionInfo`s for each connection we have to another iroh node. - */ - suspend fun `remoteInfoList`(): List - - companion object -} - -/** - * Iroh net client. - */ -open class Net : - Disposable, - AutoCloseable, - NetInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_net(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_net(pointer!!, status) - } - - /** - * Add a known node address to the node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `addNodeAddr`(`addr`: NodeAddr) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_net_add_node_addr( - thisPtr, - FfiConverterTypeNodeAddr.lower(`addr`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get the relay server we are connected to. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `homeRelay`(): kotlin.String? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_net_home_relay( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalString.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Return the [`NodeAddr`] for this node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `nodeAddr`(): NodeAddr = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_net_node_addr( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeNodeAddr.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * The string representation of the PublicKey of this node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `nodeId`(): kotlin.String = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_net_node_id( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterString.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Return connection information on the currently running node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `remoteInfo`(`nodeId`: PublicKey): RemoteInfo? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_net_remote_info( - thisPtr, - FfiConverterTypePublicKey.lower(`nodeId`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalTypeRemoteInfo.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Return `ConnectionInfo`s for each connection we have to another iroh node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `remoteInfoList`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_net_remote_info_list( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeRemoteInfo.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNet : FfiConverter { - override fun lower(value: Net): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Net = Net(value) - - override fun read(buf: ByteBuffer): Net { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Net) = 8UL - - override fun write( - value: Net, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh node client. - */ -public interface NodeInterface { - fun `endpoint`(): Endpoint - - /** - * Shutdown this iroh node. - */ - suspend fun `shutdown`() - - /** - * Get statistics of the running node. - */ - suspend fun `stats`(): Map - - /** - * Get status information about a node - */ - suspend fun `status`(): NodeStatus - - companion object -} - -/** - * Iroh node client. - */ -open class Node : - Disposable, - AutoCloseable, - NodeInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_node(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_node(pointer!!, status) - } - - override fun `endpoint`(): Endpoint = - FfiConverterTypeEndpoint.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_node_endpoint( - it, - _status, - ) - } - }, - ) - - /** - * Shutdown this iroh node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `shutdown`() = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_node_shutdown( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get statistics of the running node. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `stats`(): Map = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_node_stats( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterMapStringTypeCounterStats.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Get status information about a node - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `status`(): NodeStatus = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_node_status( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_pointer(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_pointer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_pointer(future) }, - // lift function - { FfiConverterTypeNodeStatus.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNode : FfiConverter { - override fun lower(value: Node): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Node = Node(value) - - override fun read(buf: ByteBuffer): Node { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Node) = 8UL - - override fun write( - value: Node, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A peer and it's addressing information. - */ -public interface NodeAddrInterface { - /** - * Get the direct addresses of this peer. - */ - fun `directAddresses`(): List - - /** - * Returns true if both NodeAddr's have the same values - */ - fun `equal`(`other`: NodeAddr): kotlin.Boolean - - /** - * Get the home relay URL for this peer - */ - fun `relayUrl`(): kotlin.String? - - companion object -} - -/** - * A peer and it's addressing information. - */ -open class NodeAddr : - Disposable, - AutoCloseable, - NodeAddrInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * Create a new [`NodeAddr`] with empty [`AddrInfo`]. - */ - constructor(`nodeId`: PublicKey, `derpUrl`: kotlin.String?, `addresses`: List) : - this( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_nodeaddr_new( - FfiConverterTypePublicKey.lower(`nodeId`), - FfiConverterOptionalString.lower(`derpUrl`), - FfiConverterSequenceString.lower(`addresses`), - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_nodeaddr(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_nodeaddr(pointer!!, status) - } - - /** - * Get the direct addresses of this peer. - */ - override fun `directAddresses`(): List = - FfiConverterSequenceString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodeaddr_direct_addresses( - it, - _status, - ) - } - }, - ) - - /** - * Returns true if both NodeAddr's have the same values - */ - override fun `equal`(`other`: NodeAddr): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodeaddr_equal( - it, - FfiConverterTypeNodeAddr.lower(`other`), - _status, - ) - } - }, - ) - - /** - * Get the home relay URL for this peer - */ - override fun `relayUrl`(): kotlin.String? = - FfiConverterOptionalString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodeaddr_relay_url( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNodeAddr : FfiConverter { - override fun lower(value: NodeAddr): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): NodeAddr = NodeAddr(value) - - override fun read(buf: ByteBuffer): NodeAddr { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: NodeAddr) = 8UL - - override fun write( - value: NodeAddr, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The response to a status request - */ -public interface NodeStatusInterface { - /** - * The bound listening addresses of the node - */ - fun `listenAddrs`(): List - - /** - * The node id and socket addresses of this node. - */ - fun `nodeAddr`(): NodeAddr - - /** - * The address of the RPC of the node - */ - fun `rpcAddr`(): kotlin.String? - - /** - * The version of the node - */ - fun `version`(): kotlin.String - - companion object -} - -/** - * The response to a status request - */ -open class NodeStatus : - Disposable, - AutoCloseable, - NodeStatusInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_nodestatus(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_nodestatus(pointer!!, status) - } - - /** - * The bound listening addresses of the node - */ - override fun `listenAddrs`(): List = - FfiConverterSequenceString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodestatus_listen_addrs( - it, - _status, - ) - } - }, - ) - - /** - * The node id and socket addresses of this node. - */ - override fun `nodeAddr`(): NodeAddr = - FfiConverterTypeNodeAddr.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodestatus_node_addr( - it, - _status, - ) - } - }, - ) - - /** - * The address of the RPC of the node - */ - override fun `rpcAddr`(): kotlin.String? = - FfiConverterOptionalString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodestatus_rpc_addr( - it, - _status, - ) - } - }, - ) - - /** - * The version of the node - */ - override fun `version`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodestatus_version( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNodeStatus : FfiConverter { - override fun lower(value: NodeStatus): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): NodeStatus = NodeStatus(value) - - override fun read(buf: ByteBuffer): NodeStatus { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: NodeStatus) = 8UL - - override fun write( - value: NodeStatus, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A token containing information for establishing a connection to a node. - * - * This allows establishing a connection to the node in most circumstances where it is - * possible to do so. - * - * It is a single item which can be easily serialized and deserialized. - */ -public interface NodeTicketInterface { - /** - * The [`NodeAddr`] of the provider for this ticket. - */ - fun `nodeAddr`(): NodeAddr - - companion object -} - -/** - * A token containing information for establishing a connection to a node. - * - * This allows establishing a connection to the node in most circumstances where it is - * possible to do so. - * - * It is a single item which can be easily serialized and deserialized. - */ -open class NodeTicket : - Disposable, - AutoCloseable, - NodeTicketInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * Wrap the given [`NodeAddr`] as a [`NodeTicket`]. - * - * The returned ticket can easily be deserialized using its string presentation, and - * later parsed again using [`Self::parse`]. - */ - constructor(`addr`: NodeAddr) : - this( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_nodeticket_new( - FfiConverterTypeNodeAddr.lower(`addr`), - _status, - ) - }, - ) - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_nodeticket(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_nodeticket(pointer!!, status) - } - - /** - * The [`NodeAddr`] of the provider for this ticket. - */ - override fun `nodeAddr`(): NodeAddr = - FfiConverterTypeNodeAddr.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodeticket_node_addr( - it, - _status, - ) - } - }, - ) - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_nodeticket_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Parse back a [`NodeTicket`] from its string presentation. - */ - @Throws(IrohException::class) - fun `parse`(`str`: kotlin.String): NodeTicket = - FfiConverterTypeNodeTicket.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_nodeticket_parse( - FfiConverterString.lower(`str`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeNodeTicket : FfiConverter { - override fun lower(value: NodeTicket): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): NodeTicket = NodeTicket(value) - - override fun read(buf: ByteBuffer): NodeTicket { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: NodeTicket) = 8UL - - override fun write( - value: NodeTicket, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface ProtocolCreator { - fun `create`(`endpoint`: Endpoint): ProtocolHandler - - companion object -} - -open class ProtocolCreatorImpl : - Disposable, - AutoCloseable, - ProtocolCreator { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_protocolcreator(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_protocolcreator(pointer!!, status) - } - - override fun `create`(`endpoint`: Endpoint): ProtocolHandler = - FfiConverterTypeProtocolHandler.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_protocolcreator_create( - it, - FfiConverterTypeEndpoint.lower(`endpoint`), - _status, - ) - } - }, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceProtocolCreator { - internal object `create` : UniffiCallbackInterfaceProtocolCreatorMethod0 { - override fun callback( - `uniffiHandle`: Long, - `endpoint`: Pointer, - `uniffiOutReturn`: PointerByReference, - uniffiCallStatus: UniffiRustCallStatus, - ) { - val uniffiObj = FfiConverterTypeProtocolCreator.handleMap.get(uniffiHandle) - val makeCall = { uniffiObj.`create`( - FfiConverterTypeEndpoint.lift(`endpoint`), - ) - } - val writeReturn = { value: ProtocolHandler -> uniffiOutReturn.setValue(FfiConverterTypeProtocolHandler.lower(value)) } - uniffiTraitInterfaceCall(uniffiCallStatus, makeCall, writeReturn) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeProtocolCreator.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceProtocolCreator.UniffiByValue( - `create`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_protocolcreator(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeProtocolCreator : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: ProtocolCreator): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): ProtocolCreator = ProtocolCreatorImpl(value) - - override fun read(buf: ByteBuffer): ProtocolCreator { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: ProtocolCreator) = 8UL - - override fun write( - value: ProtocolCreator, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface ProtocolHandler { - suspend fun `accept`(`conn`: Connecting) - - suspend fun `shutdown`() - - companion object -} - -open class ProtocolHandlerImpl : - Disposable, - AutoCloseable, - ProtocolHandler { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_protocolhandler(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_protocolhandler(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `accept`(`conn`: Connecting) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_protocolhandler_accept( - thisPtr, - FfiConverterTypeConnecting.lower(`conn`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `shutdown`() = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_protocolhandler_shutdown( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - UniffiNullRustCallStatusErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceProtocolHandler { - internal object `accept` : UniffiCallbackInterfaceProtocolHandlerMethod0 { - override fun callback( - `uniffiHandle`: Long, - `conn`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeProtocolHandler.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`accept`( - FfiConverterTypeConnecting.lift(`conn`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object `shutdown` : UniffiCallbackInterfaceProtocolHandlerMethod1 { - override fun callback( - `uniffiHandle`: Long, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeProtocolHandler.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`shutdown`() } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsync( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeProtocolHandler.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceProtocolHandler.UniffiByValue( - `accept`, - `shutdown`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_protocolhandler(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeProtocolHandler : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: ProtocolHandler): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): ProtocolHandler = ProtocolHandlerImpl(value) - - override fun read(buf: ByteBuffer): ProtocolHandler { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: ProtocolHandler) = 8UL - - override fun write( - value: ProtocolHandler, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A public key. - * - * The key itself is just a 32 byte array, but a key has associated crypto - * information that is cached for performance reasons. - */ -public interface PublicKeyInterface { - /** - * Returns true if the PublicKeys are equal - */ - fun `equal`(`other`: PublicKey): kotlin.Boolean - - /** - * Convert to a base32 string limited to the first 10 bytes for a friendly string - * representation of the key. - */ - fun `fmtShort`(): kotlin.String - - /** - * Express the PublicKey as a byte array - */ - fun `toBytes`(): kotlin.ByteArray - - companion object -} - -/** - * A public key. - * - * The key itself is just a 32 byte array, but a key has associated crypto - * information that is cached for performance reasons. - */ -open class PublicKey : - Disposable, - AutoCloseable, - PublicKeyInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_publickey(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_publickey(pointer!!, status) - } - - /** - * Returns true if the PublicKeys are equal - */ - override fun `equal`(`other`: PublicKey): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_publickey_equal( - it, - FfiConverterTypePublicKey.lower(`other`), - _status, - ) - } - }, - ) - - /** - * Convert to a base32 string limited to the first 10 bytes for a friendly string - * representation of the key. - */ - override fun `fmtShort`(): kotlin.String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_publickey_fmt_short( - it, - _status, - ) - } - }, - ) - - /** - * Express the PublicKey as a byte array - */ - override fun `toBytes`(): kotlin.ByteArray = - FfiConverterByteArray.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_publickey_to_bytes( - it, - _status, - ) - } - }, - ) - - override fun toString(): String = - FfiConverterString.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_publickey_uniffi_trait_display( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Make a PublicKey from byte array - */ - @Throws(IrohException::class) - fun `fromBytes`(`bytes`: kotlin.ByteArray): PublicKey = - FfiConverterTypePublicKey.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_publickey_from_bytes( - FfiConverterByteArray.lower(`bytes`), - _status, - ) - }, - ) - - /** - * Make a PublicKey from base32 string - */ - @Throws(IrohException::class) - fun `fromString`(`s`: kotlin.String): PublicKey = - FfiConverterTypePublicKey.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_publickey_from_string( - FfiConverterString.lower(`s`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypePublicKey : FfiConverter { - override fun lower(value: PublicKey): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): PublicKey = PublicKey(value) - - override fun read(buf: ByteBuffer): PublicKey { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: PublicKey) = 8UL - - override fun write( - value: PublicKey, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Build a Query to search for an entry or entries in a doc. - * - * Use this with `QueryOptions` to determine sorting, grouping, and pagination. - */ -public interface QueryInterface { - /** - * Get the limit for this query (max. number of entries to emit). - */ - fun `limit`(): kotlin.ULong? - - /** - * Get the offset for this query (number of entries to skip at the beginning). - */ - fun `offset`(): kotlin.ULong - - companion object -} - -/** - * Build a Query to search for an entry or entries in a doc. - * - * Use this with `QueryOptions` to determine sorting, grouping, and pagination. - */ -open class Query : - Disposable, - AutoCloseable, - QueryInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_query(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_query(pointer!!, status) - } - - /** - * Get the limit for this query (max. number of entries to emit). - */ - override fun `limit`(): kotlin.ULong? = - FfiConverterOptionalULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_query_limit( - it, - _status, - ) - } - }, - ) - - /** - * Get the offset for this query (number of entries to skip at the beginning). - */ - override fun `offset`(): kotlin.ULong = - FfiConverterULong.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_query_offset( - it, - _status, - ) - } - }, - ) - - companion object { - /** - * Query all records. - * - * If `opts` is `None`, the default values will be used: - * sort_by: SortBy::AuthorKey - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `all`(`opts`: QueryOptions?): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_all( - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - - /** - * Query all entries for by a single author. - * - * If `opts` is `None`, the default values will be used: - * sort_by: SortBy::AuthorKey - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `author`( - `author`: AuthorId, - `opts`: QueryOptions?, - ): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_author( - FfiConverterTypeAuthorId.lower(`author`), - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - - /** - * Create a Query for a single key and author. - */ - fun `authorKeyExact`( - `author`: AuthorId, - `key`: kotlin.ByteArray, - ): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_author_key_exact( - FfiConverterTypeAuthorId.lower(`author`), - FfiConverterByteArray.lower(`key`), - _status, - ) - }, - ) - - /** - * Create a query for all entries of a single author with a given key prefix. - * - * If `opts` is `None`, the default values will be used: - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `authorKeyPrefix`( - `author`: AuthorId, - `prefix`: kotlin.ByteArray, - `opts`: QueryOptions?, - ): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_author_key_prefix( - FfiConverterTypeAuthorId.lower(`author`), - FfiConverterByteArray.lower(`prefix`), - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - - /** - * Query all entries that have an exact key. - * - * If `opts` is `None`, the default values will be used: - * sort_by: SortBy::AuthorKey - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `keyExact`( - `key`: kotlin.ByteArray, - `opts`: QueryOptions?, - ): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_key_exact( - FfiConverterByteArray.lower(`key`), - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - - /** - * Create a query for all entries with a given key prefix. - * - * If `opts` is `None`, the default values will be used: - * sort_by: SortBy::AuthorKey - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `keyPrefix`( - `prefix`: kotlin.ByteArray, - `opts`: QueryOptions?, - ): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_key_prefix( - FfiConverterByteArray.lower(`prefix`), - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - - /** - * Query only the latest entry for each key, omitting older entries if the entry was written - * to by multiple authors. - * - * If `opts` is `None`, the default values will be used: - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `singleLatestPerKey`(`opts`: QueryOptions?): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_single_latest_per_key( - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - - /** - * Query exactly the key, but only the latest entry for it, omitting older entries if the entry was written - * to by multiple authors. - */ - fun `singleLatestPerKeyExact`(`key`: kotlin.ByteArray): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_single_latest_per_key_exact( - FfiConverterByteArray.lower(`key`), - _status, - ) - }, - ) - - /** - * Query only the latest entry for each key, with this prefix, omitting older entries if the entry was written - * to by multiple authors. - * - * If `opts` is `None`, the default values will be used: - * direction: SortDirection::Asc - * offset: None - * limit: None - */ - fun `singleLatestPerKeyPrefix`( - `prefix`: kotlin.ByteArray, - `opts`: QueryOptions?, - ): Query = - FfiConverterTypeQuery.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_query_single_latest_per_key_prefix( - FfiConverterByteArray.lower(`prefix`), - FfiConverterOptionalTypeQueryOptions.lower(`opts`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeQuery : FfiConverter { - override fun lower(value: Query): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Query = Query(value) - - override fun read(buf: ByteBuffer): Query { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Query) = 8UL - - override fun write( - value: Query, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * A chunk range specification as a sequence of chunk offsets - */ -public interface RangeSpecInterface { - /** - * Check if this [`RangeSpec`] selects all chunks in the blob - */ - fun `isAll`(): kotlin.Boolean - - /** - * Checks if this [`RangeSpec`] does not select any chunks in the blob - */ - fun `isEmpty`(): kotlin.Boolean - - companion object -} - -/** - * A chunk range specification as a sequence of chunk offsets - */ -open class RangeSpec : - Disposable, - AutoCloseable, - RangeSpecInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_rangespec(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_rangespec(pointer!!, status) - } - - /** - * Check if this [`RangeSpec`] selects all chunks in the blob - */ - override fun `isAll`(): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_rangespec_is_all( - it, - _status, - ) - } - }, - ) - - /** - * Checks if this [`RangeSpec`] does not select any chunks in the blob - */ - override fun `isEmpty`(): kotlin.Boolean = - FfiConverterBoolean.lift( - callWithPointer { - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_rangespec_is_empty( - it, - _status, - ) - } - }, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeRangeSpec : FfiConverter { - override fun lower(value: RangeSpec): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): RangeSpec = RangeSpec(value) - - override fun read(buf: ByteBuffer): RangeSpec { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: RangeSpec) = 8UL - - override fun write( - value: RangeSpec, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Defines the way to read bytes. - */ -public interface ReadAtLenInterface { - companion object -} - -/** - * Defines the way to read bytes. - */ -open class ReadAtLen : - Disposable, - AutoCloseable, - ReadAtLenInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_readatlen(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_readatlen(pointer!!, status) - } - - companion object { - fun `all`(): ReadAtLen = - FfiConverterTypeReadAtLen.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_readatlen_all( - _status, - ) - }, - ) - - fun `atMost`(`size`: kotlin.ULong): ReadAtLen = - FfiConverterTypeReadAtLen.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_readatlen_at_most( - FfiConverterULong.lower(`size`), - _status, - ) - }, - ) - - fun `exact`(`size`: kotlin.ULong): ReadAtLen = - FfiConverterTypeReadAtLen.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_readatlen_exact( - FfiConverterULong.lower(`size`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeReadAtLen : FfiConverter { - override fun lower(value: ReadAtLen): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): ReadAtLen = ReadAtLen(value) - - override fun read(buf: ByteBuffer): ReadAtLen { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: ReadAtLen) = 8UL - - override fun write( - value: ReadAtLen, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface RecvStreamInterface { - suspend fun `id`(): kotlin.String - - suspend fun `read`(`sizeLimit`: kotlin.UInt): kotlin.ByteArray - - suspend fun `readExact`(`size`: kotlin.UInt): kotlin.ByteArray - - suspend fun `readToEnd`(`sizeLimit`: kotlin.UInt): kotlin.ByteArray - - suspend fun `receivedReset`(): kotlin.ULong? - - suspend fun `stop`(`errorCode`: kotlin.ULong) - - companion object -} - -open class RecvStream : - Disposable, - AutoCloseable, - RecvStreamInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_recvstream(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_recvstream(pointer!!, status) - } - - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `id`(): kotlin.String = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_recvstream_id( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterString.lift(it) }, - // Error FFI converter - UniffiNullRustCallStatusErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `read`(`sizeLimit`: kotlin.UInt): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_recvstream_read( - thisPtr, - FfiConverterUInt.lower(`sizeLimit`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `readExact`(`size`: kotlin.UInt): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_recvstream_read_exact( - thisPtr, - FfiConverterUInt.lower(`size`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `readToEnd`(`sizeLimit`: kotlin.UInt): kotlin.ByteArray = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_recvstream_read_to_end( - thisPtr, - FfiConverterUInt.lower(`sizeLimit`), - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterByteArray.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `receivedReset`(): kotlin.ULong? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_recvstream_received_reset( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalULong.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `stop`(`errorCode`: kotlin.ULong) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_recvstream_stop( - thisPtr, - FfiConverterULong.lower(`errorCode`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeRecvStream : FfiConverter { - override fun lower(value: RecvStream): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): RecvStream = RecvStream(value) - - override fun read(buf: ByteBuffer): RecvStream { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: RecvStream) = 8UL - - override fun write( - value: RecvStream, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -public interface SendStreamInterface { - suspend fun `finish`() - - suspend fun `id`(): kotlin.String - - suspend fun `priority`(): kotlin.Int - - suspend fun `reset`(`errorCode`: kotlin.ULong) - - suspend fun `setPriority`(`p`: kotlin.Int) - - suspend fun `stopped`(): kotlin.ULong? - - suspend fun `write`(`buf`: kotlin.ByteArray): kotlin.ULong - - suspend fun `writeAll`(`buf`: kotlin.ByteArray) - - companion object -} - -open class SendStream : - Disposable, - AutoCloseable, - SendStreamInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_sendstream(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_sendstream(pointer!!, status) - } - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `finish`() = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_finish( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `id`(): kotlin.String = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_id( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterString.lift(it) }, - // Error FFI converter - UniffiNullRustCallStatusErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `priority`(): kotlin.Int = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_priority( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_i32(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_i32(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_i32(future) }, - // lift function - { FfiConverterInt.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `reset`(`errorCode`: kotlin.ULong) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_reset( - thisPtr, - FfiConverterULong.lower(`errorCode`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `setPriority`(`p`: kotlin.Int) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_set_priority( - thisPtr, - FfiConverterInt.lower(`p`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `stopped`(): kotlin.ULong? = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_stopped( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterOptionalULong.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `write`(`buf`: kotlin.ByteArray): kotlin.ULong = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_write( - thisPtr, - FfiConverterByteArray.lower(`buf`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_u64(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_u64(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_u64(future) }, - // lift function - { FfiConverterULong.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `writeAll`(`buf`: kotlin.ByteArray) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sendstream_write_all( - thisPtr, - FfiConverterByteArray.lower(`buf`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeSendStream : FfiConverter { - override fun lower(value: SendStream): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): SendStream = SendStream(value) - - override fun read(buf: ByteBuffer): SendStream { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: SendStream) = 8UL - - override fun write( - value: SendStream, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Gossip sender - */ -public interface SenderInterface { - /** - * Broadcast a message to all nodes in the swarm - */ - suspend fun `broadcast`(`msg`: kotlin.ByteArray) - - /** - * Broadcast a message to all direct neighbors. - */ - suspend fun `broadcastNeighbors`(`msg`: kotlin.ByteArray) - - /** - * Closes the subscription, it is an error to use it afterwards - */ - suspend fun `cancel`() - - companion object -} - -/** - * Gossip sender - */ -open class Sender : - Disposable, - AutoCloseable, - SenderInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_sender(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_sender(pointer!!, status) - } - - /** - * Broadcast a message to all nodes in the swarm - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `broadcast`(`msg`: kotlin.ByteArray) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sender_broadcast( - thisPtr, - FfiConverterByteArray.lower(`msg`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Broadcast a message to all direct neighbors. - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `broadcastNeighbors`(`msg`: kotlin.ByteArray) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sender_broadcast_neighbors( - thisPtr, - FfiConverterByteArray.lower(`msg`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * Closes the subscription, it is an error to use it afterwards - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `cancel`() = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_sender_cancel( - thisPtr, - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeSender : FfiConverter { - override fun lower(value: Sender): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Sender = Sender(value) - - override fun read(buf: ByteBuffer): Sender { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Sender) = 8UL - - override fun write( - value: Sender, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * An option for commands that allow setting a Tag - */ -public interface SetTagOptionInterface { - companion object -} - -/** - * An option for commands that allow setting a Tag - */ -open class SetTagOption : - Disposable, - AutoCloseable, - SetTagOptionInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_settagoption(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_settagoption(pointer!!, status) - } - - companion object { - /** - * Indicate you want an automatically generated tag - */ - fun `auto`(): SetTagOption = - FfiConverterTypeSetTagOption.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_settagoption_auto( - _status, - ) - }, - ) - - /** - * Indicate you want a named tag - */ - fun `named`(`tag`: kotlin.ByteArray): SetTagOption = - FfiConverterTypeSetTagOption.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_settagoption_named( - FfiConverterByteArray.lower(`tag`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeSetTagOption : FfiConverter { - override fun lower(value: SetTagOption): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): SetTagOption = SetTagOption(value) - - override fun read(buf: ByteBuffer): SetTagOption { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: SetTagOption) = 8UL - - override fun write( - value: SetTagOption, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * The `progress` method will be called for each `SubscribeProgress` event that is - * emitted during a `node.doc_subscribe`. Use the `SubscribeProgress.type()` - * method to check the `LiveEvent` - */ -public interface SubscribeCallback { - suspend fun `event`(`event`: LiveEvent) - - companion object -} - -/** - * The `progress` method will be called for each `SubscribeProgress` event that is - * emitted during a `node.doc_subscribe`. Use the `SubscribeProgress.type()` - * method to check the `LiveEvent` - */ -open class SubscribeCallbackImpl : - Disposable, - AutoCloseable, - SubscribeCallback { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_subscribecallback(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_subscribecallback(pointer!!, status) - } - - @Throws(CallbackException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `event`(`event`: LiveEvent) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_subscribecallback_event( - thisPtr, - FfiConverterTypeLiveEvent.lower(`event`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - CallbackException.ErrorHandler, - ) - - companion object -} - -// Put the implementation in an object so we don't pollute the top-level namespace -internal object uniffiCallbackInterfaceSubscribeCallback { - internal object `event` : UniffiCallbackInterfaceSubscribeCallbackMethod0 { - override fun callback( - `uniffiHandle`: Long, - `event`: Pointer, - `uniffiFutureCallback`: UniffiForeignFutureCompleteVoid, - `uniffiCallbackData`: Long, - `uniffiOutReturn`: UniffiForeignFuture, - ) { - val uniffiObj = FfiConverterTypeSubscribeCallback.handleMap.get(uniffiHandle) - val makeCall = - suspend { uniffiObj.`event`( - FfiConverterTypeLiveEvent.lift(`event`), - ) - } - val uniffiHandleSuccess = { _: Unit -> - val uniffiResult = - UniffiForeignFutureStructVoid.UniffiByValue( - UniffiRustCallStatus.ByValue(), - ) - uniffiResult.write() - uniffiFutureCallback.callback(uniffiCallbackData, uniffiResult) - } - val uniffiHandleError = { callStatus: UniffiRustCallStatus.ByValue -> - uniffiFutureCallback.callback( - uniffiCallbackData, - UniffiForeignFutureStructVoid.UniffiByValue( - callStatus, - ), - ) - } - - uniffiOutReturn.uniffiSetValue( - uniffiTraitInterfaceCallAsyncWithError( - makeCall, - uniffiHandleSuccess, - uniffiHandleError, - { e: CallbackException -> FfiConverterTypeCallbackError.lower(e) }, - ), - ) - } - } - - internal object uniffiFree : UniffiCallbackInterfaceFree { - override fun callback(handle: Long) { - FfiConverterTypeSubscribeCallback.handleMap.remove(handle) - } - } - - internal var vtable = - UniffiVTableCallbackInterfaceSubscribeCallback.UniffiByValue( - `event`, - uniffiFree, - ) - - // Registers the foreign callback with the Rust side. - // This method is generated for each callback interface. - internal fun register(lib: UniffiLib) { - lib.uniffi_iroh_ffi_fn_init_callback_vtable_subscribecallback(vtable) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeSubscribeCallback : FfiConverter { - internal val handleMap = UniffiHandleMap() - - override fun lower(value: SubscribeCallback): Pointer = Pointer(handleMap.insert(value)) - - override fun lift(value: Pointer): SubscribeCallback = SubscribeCallbackImpl(value) - - override fun read(buf: ByteBuffer): SubscribeCallback { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: SubscribeCallback) = 8UL - - override fun write( - value: SubscribeCallback, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Iroh tags client. - */ -public interface TagsInterface { - /** - * Delete a tag - */ - suspend fun `delete`(`name`: kotlin.ByteArray) - - /** - * List all tags - * - * Note: this allocates for each `ListTagsResponse`, if you have many `Tags`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - suspend fun `list`(): List - - companion object -} - -/** - * Iroh tags client. - */ -open class Tags : - Disposable, - AutoCloseable, - TagsInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_tags(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_tags(pointer!!, status) - } - - /** - * Delete a tag - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `delete`(`name`: kotlin.ByteArray) = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_tags_delete( - thisPtr, - FfiConverterByteArray.lower(`name`), - ) - }, - { future, callback, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_void(future, callback, continuation) }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_void(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_void(future) }, - // lift function - { Unit }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - /** - * List all tags - * - * Note: this allocates for each `ListTagsResponse`, if you have many `Tags`s this may be a prohibitively large list. - * Please file an [issue](https://github.com/n0-computer/iroh-ffi/issues/new) if you run into this issue - */ - @Throws(IrohException::class) - @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") - override suspend fun `list`(): List = - uniffiRustCallAsync( - callWithPointer { thisPtr -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_method_tags_list( - thisPtr, - ) - }, - { - future, - callback, - continuation, - -> - UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_poll_rust_buffer(future, callback, continuation) - }, - { future, continuation -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_complete_rust_buffer(future, continuation) }, - { future -> UniffiLib.INSTANCE.ffi_iroh_ffi_rust_future_free_rust_buffer(future) }, - // lift function - { FfiConverterSequenceTypeTagInfo.lift(it) }, - // Error FFI converter - IrohException.ErrorHandler, - ) - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTags : FfiConverter { - override fun lower(value: Tags): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): Tags = Tags(value) - - override fun read(buf: ByteBuffer): Tags { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: Tags) = 8UL - - override fun write( - value: Tags, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -// This template implements a class for working with a Rust struct via a Pointer/Arc -// to the live Rust struct on the other side of the FFI. -// -// Each instance implements core operations for working with the Rust `Arc` and the -// Kotlin Pointer to work with the live Rust struct on the other side of the FFI. -// -// There's some subtlety here, because we have to be careful not to operate on a Rust -// struct after it has been dropped, and because we must expose a public API for freeing -// theq Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: -// -// * Each instance holds an opaque pointer to the underlying Rust struct. -// Method calls need to read this pointer from the object's state and pass it in to -// the Rust FFI. -// -// * When an instance is no longer needed, its pointer should be passed to a -// special destructor function provided by the Rust FFI, which will drop the -// underlying Rust struct. -// -// * Given an instance, calling code is expected to call the special -// `destroy` method in order to free it after use, either by calling it explicitly -// or by using a higher-level helper like the `use` method. Failing to do so risks -// leaking the underlying Rust struct. -// -// * We can't assume that calling code will do the right thing, and must be prepared -// to handle Kotlin method calls executing concurrently with or even after a call to -// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`. -// -// * We must never allow Rust code to operate on the underlying Rust struct after -// the destructor has been called, and must never call the destructor more than once. -// Doing so may trigger memory unsafety. -// -// * To mitigate many of the risks of leaking memory and use-after-free unsafety, a `Cleaner` -// is implemented to call the destructor when the Kotlin object becomes unreachable. -// This is done in a background thread. This is not a panacea, and client code should be aware that -// 1. the thread may starve if some there are objects that have poorly performing -// `drop` methods or do significant work in their `drop` methods. -// 2. the thread is shared across the whole library. This can be tuned by using `android_cleaner = true`, -// or `android = true` in the [`kotlin` section of the `uniffi.toml` file](https://mozilla.github.io/uniffi-rs/kotlin/configuration.html). -// -// If we try to implement this with mutual exclusion on access to the pointer, there is the -// possibility of a race between a method call and a concurrent call to `destroy`: -// -// * Thread A starts a method call, reads the value of the pointer, but is interrupted -// before it can pass the pointer over the FFI to Rust. -// * Thread B calls `destroy` and frees the underlying Rust struct. -// * Thread A resumes, passing the already-read pointer value to Rust and triggering -// a use-after-free. -// -// One possible solution would be to use a `ReadWriteLock`, with each method call taking -// a read lock (and thus allowed to run concurrently) and the special `destroy` method -// taking a write lock (and thus blocking on live method calls). However, we aim not to -// generate methods with any hidden blocking semantics, and a `destroy` method that might -// block if called incorrectly seems to meet that bar. -// -// So, we achieve our goals by giving each instance an associated `AtomicLong` counter to track -// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy` -// has been called. These are updated according to the following rules: -// -// * The initial value of the counter is 1, indicating a live object with no in-flight calls. -// The initial value for the flag is false. -// -// * At the start of each method call, we atomically check the counter. -// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted. -// If it is nonzero them we atomically increment it by 1 and proceed with the method call. -// -// * At the end of each method call, we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// * When `destroy` is called, we atomically flip the flag from false to true. -// If the flag was already true we silently fail. -// Otherwise we atomically decrement and check the counter. -// If it has reached zero then we destroy the underlying Rust struct. -// -// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc` works, -// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`. -// -// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been -// called *and* all in-flight method calls have completed, avoiding violating any of the expectations -// of the underlying Rust code. -// -// This makes a cleaner a better alternative to _not_ calling `destroy()` as -// and when the object is finished with, but the abstraction is not perfect: if the Rust object's `drop` -// method is slow, and/or there are many objects to cleanup, and it's on a low end Android device, then the cleaner -// thread may be starved, and the app will leak memory. -// -// In this case, `destroy`ing manually may be a better solution. -// -// The cleaner can live side by side with the manual calling of `destroy`. In the order of responsiveness, uniffi objects -// with Rust peers are reclaimed: -// -// 1. By calling the `destroy` method of the object, which calls `rustObject.free()`. If that doesn't happen: -// 2. When the object becomes unreachable, AND the Cleaner thread gets to call `rustObject.free()`. If the thread is starved then: -// 3. The memory is reclaimed when the process terminates. -// -// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 -// - -/** - * Whether to wrap the added data in a collection. - */ -public interface WrapOptionInterface { - companion object -} - -/** - * Whether to wrap the added data in a collection. - */ -open class WrapOption : - Disposable, - AutoCloseable, - WrapOptionInterface { - constructor(pointer: Pointer) { - this.pointer = pointer - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - /** - * This constructor can be used to instantiate a fake object. Only used for tests. Any - * attempt to actually use an object constructed this way will fail as there is no - * connected Rust object. - */ - @Suppress("UNUSED_PARAMETER") - constructor(noPointer: NoPointer) { - this.pointer = null - this.cleanable = UniffiLib.CLEANER.register(this, UniffiCleanAction(pointer)) - } - - protected val pointer: Pointer? - protected val cleanable: UniffiCleaner.Cleanable - - private val wasDestroyed = AtomicBoolean(false) - private val callCounter = AtomicLong(1) - - override fun destroy() { - // Only allow a single call to this method. - // TODO: maybe we should log a warning if called more than once? - if (this.wasDestroyed.compareAndSet(false, true)) { - // This decrement always matches the initial count of 1 given at creation time. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - @Synchronized - override fun close() { - this.destroy() - } - - internal inline fun callWithPointer(block: (ptr: Pointer) -> R): R { - // Check and increment the call counter, to keep the object alive. - // This needs a compare-and-set retry loop in case of concurrent updates. - do { - val c = this.callCounter.get() - if (c == 0L) { - throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed") - } - if (c == Long.MAX_VALUE) { - throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow") - } - } while (!this.callCounter.compareAndSet(c, c + 1L)) - // Now we can safely do the method call without the pointer being freed concurrently. - try { - return block(this.uniffiClonePointer()) - } finally { - // This decrement always matches the increment we performed above. - if (this.callCounter.decrementAndGet() == 0L) { - cleanable.clean() - } - } - } - - // Use a static inner class instead of a closure so as not to accidentally - // capture `this` as part of the cleanable's action. - private class UniffiCleanAction( - private val pointer: Pointer?, - ) : Runnable { - override fun run() { - pointer?.let { ptr -> - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_free_wrapoption(ptr, status) - } - } - } - } - - fun uniffiClonePointer(): Pointer = - uniffiRustCall { status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_clone_wrapoption(pointer!!, status) - } - - companion object { - /** - * Indicate you do not wrap the file or directory. - */ - fun `noWrap`(): WrapOption = - FfiConverterTypeWrapOption.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_wrapoption_no_wrap( - _status, - ) - }, - ) - - /** - * Indicate you want to wrap the file or directory in a colletion, with an optional name - */ - fun `wrap`(`name`: kotlin.String?): WrapOption = - FfiConverterTypeWrapOption.lift( - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_constructor_wrapoption_wrap( - FfiConverterOptionalString.lower(`name`), - _status, - ) - }, - ) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeWrapOption : FfiConverter { - override fun lower(value: WrapOption): Pointer = value.uniffiClonePointer() - - override fun lift(value: Pointer): WrapOption = WrapOption(value) - - override fun read(buf: ByteBuffer): WrapOption { - // The Rust code always writes pointers as 8 bytes, and will - // fail to compile if they don't fit. - return lift(Pointer(buf.getLong())) - } - - override fun allocationSize(value: WrapOption) = 8UL - - override fun write( - value: WrapOption, - buf: ByteBuffer, - ) { - // The Rust code always expects pointers written as 8 bytes, - // and will fail to compile if they don't fit. - buf.putLong(Pointer.nativeValue(lower(value))) - } -} - -/** - * An AddProgress event indicating we got an error and need to abort - */ -data class AddProgressAbort( - var `error`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgressAbort : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): AddProgressAbort = - AddProgressAbort( - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: AddProgressAbort) = - ( - FfiConverterString.allocationSize(value.`error`) - ) - - override fun write( - value: AddProgressAbort, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`error`, buf) - } -} - -/** - * An AddProgress event indicating we are done with the the whole operation - */ -data class AddProgressAllDone( - /** - * The hash of the created data. - */ - var `hash`: Hash, - /** - * The format of the added data. - */ - var `format`: BlobFormat, - /** - * The tag of the added data. - */ - var `tag`: kotlin.ByteArray, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`format`) - - Disposable.destroy(this.`tag`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgressAllDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): AddProgressAllDone = - AddProgressAllDone( - FfiConverterTypeHash.read(buf), - FfiConverterTypeBlobFormat.read(buf), - FfiConverterByteArray.read(buf), - ) - - override fun allocationSize(value: AddProgressAllDone) = - ( - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterTypeBlobFormat.allocationSize(value.`format`) + - FfiConverterByteArray.allocationSize(value.`tag`) - ) - - override fun write( - value: AddProgressAllDone, - buf: ByteBuffer, - ) { - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterTypeBlobFormat.write(value.`format`, buf) - FfiConverterByteArray.write(value.`tag`, buf) - } -} - -/** - * An AddProgress event indicated we are done with `id` and now have a hash `hash` - */ -data class AddProgressDone( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, - /** - * The hash of the entry. - */ - var `hash`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`id`) - - Disposable.destroy(this.`hash`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgressDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): AddProgressDone = - AddProgressDone( - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: AddProgressDone) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterTypeHash.allocationSize(value.`hash`) - ) - - override fun write( - value: AddProgressDone, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - } -} - -/** - * An AddProgress event indicating an item was found with name `name`, that can be referred to by `id` - */ -data class AddProgressFound( - /** - * A new unique id for this entry. - */ - var `id`: kotlin.ULong, - /** - * The name of the entry. - */ - var `name`: kotlin.String, - /** - * The size of the entry in bytes. - */ - var `size`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgressFound : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): AddProgressFound = - AddProgressFound( - FfiConverterULong.read(buf), - FfiConverterString.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: AddProgressFound) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterString.allocationSize(value.`name`) + - FfiConverterULong.allocationSize(value.`size`) - ) - - override fun write( - value: AddProgressFound, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterString.write(value.`name`, buf) - FfiConverterULong.write(value.`size`, buf) - } -} - -/** - * An AddProgress event indicating we got progress ingesting item `id`. - */ -data class AddProgressProgress( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, - /** - * The offset of the progress, in bytes. - */ - var `offset`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgressProgress : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): AddProgressProgress = - AddProgressProgress( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: AddProgressProgress) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterULong.allocationSize(value.`offset`) - ) - - override fun write( - value: AddProgressProgress, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterULong.write(value.`offset`, buf) - } -} - -/** - * Outcome of a blob add operation. - */ -data class BlobAddOutcome( - /** - * The hash of the blob - */ - var `hash`: Hash, - /** - * The format the blob - */ - var `format`: BlobFormat, - /** - * The size of the blob - */ - var `size`: kotlin.ULong, - /** - * The tag of the blob - */ - var `tag`: kotlin.ByteArray, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`format`) - - Disposable.destroy(this.`size`) - - Disposable.destroy(this.`tag`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobAddOutcome : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BlobAddOutcome = - BlobAddOutcome( - FfiConverterTypeHash.read(buf), - FfiConverterTypeBlobFormat.read(buf), - FfiConverterULong.read(buf), - FfiConverterByteArray.read(buf), - ) - - override fun allocationSize(value: BlobAddOutcome) = - ( - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterTypeBlobFormat.allocationSize(value.`format`) + - FfiConverterULong.allocationSize(value.`size`) + - FfiConverterByteArray.allocationSize(value.`tag`) - ) - - override fun write( - value: BlobAddOutcome, - buf: ByteBuffer, - ) { - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterTypeBlobFormat.write(value.`format`, buf) - FfiConverterULong.write(value.`size`, buf) - FfiConverterByteArray.write(value.`tag`, buf) - } -} - -/** - * A response to a list blobs request - */ -data class BlobInfo( - /** - * Location of the blob - */ - var `path`: kotlin.String, - /** - * The hash of the blob - */ - var `hash`: Hash, - /** - * The size of the blob - */ - var `size`: kotlin.ULong, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`path`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`size`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobInfo : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BlobInfo = - BlobInfo( - FfiConverterString.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: BlobInfo) = - ( - FfiConverterString.allocationSize(value.`path`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterULong.allocationSize(value.`size`) - ) - - override fun write( - value: BlobInfo, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`path`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterULong.write(value.`size`, buf) - } -} - -/** - * A new client connected to the node. - */ -data class ClientConnected( - /** - * An unique connection id. - */ - var `connectionId`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeClientConnected : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ClientConnected = - ClientConnected( - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: ClientConnected) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) - ) - - override fun write( - value: ClientConnected, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - } -} - -/** - * A response to a list collections request - */ -data class CollectionInfo( - /** - * Tag of the collection - */ - var `tag`: kotlin.ByteArray, - /** - * Hash of the collection - */ - var `hash`: Hash, - /** - * Number of children in the collection - * - * This is an optional field, because the data is not always available. - */ - var `totalBlobsCount`: kotlin.ULong?, - /** - * Total size of the raw data referred to by all links - * - * This is an optional field, because the data is not always available. - */ - var `totalBlobsSize`: kotlin.ULong?, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`tag`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`totalBlobsCount`) - - Disposable.destroy(this.`totalBlobsSize`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeCollectionInfo : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): CollectionInfo = - CollectionInfo( - FfiConverterByteArray.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterOptionalULong.read(buf), - FfiConverterOptionalULong.read(buf), - ) - - override fun allocationSize(value: CollectionInfo) = - ( - FfiConverterByteArray.allocationSize(value.`tag`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterOptionalULong.allocationSize(value.`totalBlobsCount`) + - FfiConverterOptionalULong.allocationSize(value.`totalBlobsSize`) - ) - - override fun write( - value: CollectionInfo, - buf: ByteBuffer, - ) { - FfiConverterByteArray.write(value.`tag`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterOptionalULong.write(value.`totalBlobsCount`, buf) - FfiConverterOptionalULong.write(value.`totalBlobsSize`, buf) - } -} - -/** - * The socket address and url of the mixed connection - */ -data class ConnectionTypeMixed( - /** - * Address of the node - */ - var `addr`: kotlin.String, - /** - * Url of the relay node to which the node is connected - */ - var `relayUrl`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeConnectionTypeMixed : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): ConnectionTypeMixed = - ConnectionTypeMixed( - FfiConverterString.read(buf), - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: ConnectionTypeMixed) = - ( - FfiConverterString.allocationSize(value.`addr`) + - FfiConverterString.allocationSize(value.`relayUrl`) - ) - - override fun write( - value: ConnectionTypeMixed, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`addr`, buf) - FfiConverterString.write(value.`relayUrl`, buf) - } -} - -/** - * Stats counter - */ -data class CounterStats( - /** - * The counter value - */ - var `value`: kotlin.UInt, - /** - * The counter description - */ - var `description`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeCounterStats : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): CounterStats = - CounterStats( - FfiConverterUInt.read(buf), - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: CounterStats) = - ( - FfiConverterUInt.allocationSize(value.`value`) + - FfiConverterString.allocationSize(value.`description`) - ) - - override fun write( - value: CounterStats, - buf: ByteBuffer, - ) { - FfiConverterUInt.write(value.`value`, buf) - FfiConverterString.write(value.`description`, buf) - } -} - -/** - * A DocExportProgress event indicating we got an error and need to abort - */ -data class DocExportProgressAbort( - /** - * The error message - */ - var `error`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportProgressAbort : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocExportProgressAbort = - DocExportProgressAbort( - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: DocExportProgressAbort) = - ( - FfiConverterString.allocationSize(value.`error`) - ) - - override fun write( - value: DocExportProgressAbort, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`error`, buf) - } -} - -/** - * A DocExportProgress event indicating a single blob wit `id` is done - */ -data class DocExportProgressDone( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportProgressDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocExportProgressDone = - DocExportProgressDone( - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DocExportProgressDone) = - ( - FfiConverterULong.allocationSize(value.`id`) - ) - - override fun write( - value: DocExportProgressDone, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - } -} - -/** - * A DocExportProgress event indicating a file was found with name `name`, from now on referred to via `id` - */ -data class DocExportProgressFound( - /** - * A new unique id for this entry. - */ - var `id`: kotlin.ULong, - /** - * The hash of the entry. - */ - var `hash`: Hash, - /** - * The size of the entry in bytes. - */ - var `size`: kotlin.ULong, - /** - * The path where we are writing the entry - */ - var `outpath`: kotlin.String, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`id`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`size`) - - Disposable.destroy(this.`outpath`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportProgressFound : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocExportProgressFound = - DocExportProgressFound( - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterULong.read(buf), - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: DocExportProgressFound) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterULong.allocationSize(value.`size`) + - FfiConverterString.allocationSize(value.`outpath`) - ) - - override fun write( - value: DocExportProgressFound, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterULong.write(value.`size`, buf) - FfiConverterString.write(value.`outpath`, buf) - } -} - -/** - * A DocExportProgress event indicating we've made progress exporting item `id`. - */ -data class DocExportProgressProgress( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, - /** - * The offset of the progress, in bytes. - */ - var `offset`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportProgressProgress : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocExportProgressProgress = - DocExportProgressProgress( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DocExportProgressProgress) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterULong.allocationSize(value.`offset`) - ) - - override fun write( - value: DocExportProgressProgress, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterULong.write(value.`offset`, buf) - } -} - -/** - * A DocImportProgress event indicating we got an error and need to abort - */ -data class DocImportProgressAbort( - /** - * The error message - */ - var `error`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgressAbort : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocImportProgressAbort = - DocImportProgressAbort( - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: DocImportProgressAbort) = - ( - FfiConverterString.allocationSize(value.`error`) - ) - - override fun write( - value: DocImportProgressAbort, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`error`, buf) - } -} - -/** - * A DocImportProgress event indicating we are done setting the entry to the doc - */ -data class DocImportProgressAllDone( - /** - * The key of the entry - */ - var `key`: kotlin.ByteArray, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgressAllDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocImportProgressAllDone = - DocImportProgressAllDone( - FfiConverterByteArray.read(buf), - ) - - override fun allocationSize(value: DocImportProgressAllDone) = - ( - FfiConverterByteArray.allocationSize(value.`key`) - ) - - override fun write( - value: DocImportProgressAllDone, - buf: ByteBuffer, - ) { - FfiConverterByteArray.write(value.`key`, buf) - } -} - -/** - * A DocImportProgress event indicating a file was found with name `name`, from now on referred to via `id` - */ -data class DocImportProgressFound( - /** - * A new unique id for this entry. - */ - var `id`: kotlin.ULong, - /** - * The name of the entry. - */ - var `name`: kotlin.String, - /** - * The size of the entry in bytes. - */ - var `size`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgressFound : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocImportProgressFound = - DocImportProgressFound( - FfiConverterULong.read(buf), - FfiConverterString.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DocImportProgressFound) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterString.allocationSize(value.`name`) + - FfiConverterULong.allocationSize(value.`size`) - ) - - override fun write( - value: DocImportProgressFound, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterString.write(value.`name`, buf) - FfiConverterULong.write(value.`size`, buf) - } -} - -/** - * A DocImportProgress event indicating we are finished adding `id` to the data store and the hash is `hash`. - */ -data class DocImportProgressIngestDone( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, - /** - * The hash of the entry. - */ - var `hash`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`id`) - - Disposable.destroy(this.`hash`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgressIngestDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocImportProgressIngestDone = - DocImportProgressIngestDone( - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: DocImportProgressIngestDone) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterTypeHash.allocationSize(value.`hash`) - ) - - override fun write( - value: DocImportProgressIngestDone, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - } -} - -/** - * A DocImportProgress event indicating we've made progress ingesting item `id`. - */ -data class DocImportProgressProgress( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, - /** - * The offset of the progress, in bytes. - */ - var `offset`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgressProgress : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocImportProgressProgress = - DocImportProgressProgress( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DocImportProgressProgress) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterULong.allocationSize(value.`offset`) - ) - - override fun write( - value: DocImportProgressProgress, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterULong.write(value.`offset`, buf) - } -} - -/** - * A DownloadProgress event indicating we got an error and need to abort - */ -data class DownloadProgressAbort( - var `error`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressAbort : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressAbort = - DownloadProgressAbort( - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: DownloadProgressAbort) = - ( - FfiConverterString.allocationSize(value.`error`) - ) - - override fun write( - value: DownloadProgressAbort, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`error`, buf) - } -} - -/** - * A DownloadProgress event indicating we are done with the whole operation - */ -data class DownloadProgressAllDone( - /** - * The number of bytes written - */ - var `bytesWritten`: kotlin.ULong, - /** - * The number of bytes read - */ - var `bytesRead`: kotlin.ULong, - /** - * The time it took to transfer the data - */ - var `elapsed`: java.time.Duration, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressAllDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressAllDone = - DownloadProgressAllDone( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterDuration.read(buf), - ) - - override fun allocationSize(value: DownloadProgressAllDone) = - ( - FfiConverterULong.allocationSize(value.`bytesWritten`) + - FfiConverterULong.allocationSize(value.`bytesRead`) + - FfiConverterDuration.allocationSize(value.`elapsed`) - ) - - override fun write( - value: DownloadProgressAllDone, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`bytesWritten`, buf) - FfiConverterULong.write(value.`bytesRead`, buf) - FfiConverterDuration.write(value.`elapsed`, buf) - } -} - -/** - * A DownloadProgress event indicated we are done with `id` - */ -data class DownloadProgressDone( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressDone : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressDone = - DownloadProgressDone( - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DownloadProgressDone) = - ( - FfiConverterULong.allocationSize(value.`id`) - ) - - override fun write( - value: DownloadProgressDone, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - } -} - -/** - * A DownloadProgress event indicating an item was found with hash `hash`, that can be referred to by `id` - */ -data class DownloadProgressFound( - /** - * A new unique id for this entry. - */ - var `id`: kotlin.ULong, - /** - * child offset - */ - var `child`: kotlin.ULong, - /** - * The hash of the entry. - */ - var `hash`: Hash, - /** - * The size of the entry in bytes. - */ - var `size`: kotlin.ULong, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`id`) - - Disposable.destroy(this.`child`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`size`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressFound : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressFound = - DownloadProgressFound( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DownloadProgressFound) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterULong.allocationSize(value.`child`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterULong.allocationSize(value.`size`) - ) - - override fun write( - value: DownloadProgressFound, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterULong.write(value.`child`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterULong.write(value.`size`, buf) - } -} - -/** - * A DownloadProgress event indicating an item was found with hash `hash`, that can be referred to by `id` - */ -data class DownloadProgressFoundHashSeq( - /** - * Number of children in the collection, if known. - */ - var `children`: kotlin.ULong, - /** - * The hash of the entry. - */ - var `hash`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`children`) - - Disposable.destroy(this.`hash`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressFoundHashSeq : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressFoundHashSeq = - DownloadProgressFoundHashSeq( - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: DownloadProgressFoundHashSeq) = - ( - FfiConverterULong.allocationSize(value.`children`) + - FfiConverterTypeHash.allocationSize(value.`hash`) - ) - - override fun write( - value: DownloadProgressFoundHashSeq, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`children`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - } -} - -/** - * A DownloadProgress event indicating an entry was found locally - */ -data class DownloadProgressFoundLocal( - /** - * child offset - */ - var `child`: kotlin.ULong, - /** - * The hash of the entry. - */ - var `hash`: Hash, - /** - * The size of the entry in bytes. - */ - var `size`: kotlin.ULong, - /** - * The ranges that are available locally. - */ - var `validRanges`: RangeSpec, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`child`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`size`) - - Disposable.destroy(this.`validRanges`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressFoundLocal : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressFoundLocal = - DownloadProgressFoundLocal( - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeRangeSpec.read(buf), - ) - - override fun allocationSize(value: DownloadProgressFoundLocal) = - ( - FfiConverterULong.allocationSize(value.`child`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterULong.allocationSize(value.`size`) + - FfiConverterTypeRangeSpec.allocationSize(value.`validRanges`) - ) - - override fun write( - value: DownloadProgressFoundLocal, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`child`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterULong.write(value.`size`, buf) - FfiConverterTypeRangeSpec.write(value.`validRanges`, buf) - } -} - -data class DownloadProgressInitialState( - /** - * Whether we are connected to a node - */ - var `connected`: kotlin.Boolean, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressInitialState : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressInitialState = - DownloadProgressInitialState( - FfiConverterBoolean.read(buf), - ) - - override fun allocationSize(value: DownloadProgressInitialState) = - ( - FfiConverterBoolean.allocationSize(value.`connected`) - ) - - override fun write( - value: DownloadProgressInitialState, - buf: ByteBuffer, - ) { - FfiConverterBoolean.write(value.`connected`, buf) - } -} - -/** - * A DownloadProgress event indicating we got progress ingesting item `id`. - */ -data class DownloadProgressProgress( - /** - * The unique id of the entry. - */ - var `id`: kotlin.ULong, - /** - * The offset of the progress, in bytes. - */ - var `offset`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressProgress : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DownloadProgressProgress = - DownloadProgressProgress( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: DownloadProgressProgress) = - ( - FfiConverterULong.allocationSize(value.`id`) + - FfiConverterULong.allocationSize(value.`offset`) - ) - - override fun write( - value: DownloadProgressProgress, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`id`, buf) - FfiConverterULong.write(value.`offset`, buf) - } -} - -/** - * A request was received from a client. - */ -data class GetRequestReceived( - /** - * An unique connection id. - */ - var `connectionId`: kotlin.ULong, - /** - * An identifier uniquely identifying this transfer request. - */ - var `requestId`: kotlin.ULong, - /** - * The hash for which the client wants to receive data. - */ - var `hash`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`connectionId`) - - Disposable.destroy(this.`requestId`) - - Disposable.destroy(this.`hash`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeGetRequestReceived : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): GetRequestReceived = - GetRequestReceived( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: GetRequestReceived) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) + - FfiConverterULong.allocationSize(value.`requestId`) + - FfiConverterTypeHash.allocationSize(value.`hash`) - ) - - override fun write( - value: GetRequestReceived, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - FfiConverterULong.write(value.`requestId`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - } -} - -/** - * The Hash and associated tag of a newly created collection - */ -data class HashAndTag( - /** - * The hash of the collection - */ - var `hash`: Hash, - /** - * The tag of the collection - */ - var `tag`: kotlin.ByteArray, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`tag`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeHashAndTag : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): HashAndTag = - HashAndTag( - FfiConverterTypeHash.read(buf), - FfiConverterByteArray.read(buf), - ) - - override fun allocationSize(value: HashAndTag) = - ( - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterByteArray.allocationSize(value.`tag`) - ) - - override fun write( - value: HashAndTag, - buf: ByteBuffer, - ) { - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterByteArray.write(value.`tag`, buf) - } -} - -/** - * A response to a list blobs request - */ -data class IncompleteBlobInfo( - /** - * The size we got - */ - var `size`: kotlin.ULong, - /** - * The size we expect - */ - var `expectedSize`: kotlin.ULong, - /** - * The hash of the blob - */ - var `hash`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`size`) - - Disposable.destroy(this.`expectedSize`) - - Disposable.destroy(this.`hash`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeIncompleteBlobInfo : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): IncompleteBlobInfo = - IncompleteBlobInfo( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: IncompleteBlobInfo) = - ( - FfiConverterULong.allocationSize(value.`size`) + - FfiConverterULong.allocationSize(value.`expectedSize`) + - FfiConverterTypeHash.allocationSize(value.`hash`) - ) - - override fun write( - value: IncompleteBlobInfo, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`size`, buf) - FfiConverterULong.write(value.`expectedSize`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - } -} - -/** - * Outcome of an InsertRemove event. - */ -data class InsertRemoteEvent( - /** - * The peer that sent us the entry. - */ - var `from`: PublicKey, - /** - * The inserted entry. - */ - var `entry`: Entry, - /** - * If the content is available at the local node - */ - var `contentStatus`: ContentStatus, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`from`) - - Disposable.destroy(this.`entry`) - - Disposable.destroy(this.`contentStatus`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeInsertRemoteEvent : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): InsertRemoteEvent = - InsertRemoteEvent( - FfiConverterTypePublicKey.read(buf), - FfiConverterTypeEntry.read(buf), - FfiConverterTypeContentStatus.read(buf), - ) - - override fun allocationSize(value: InsertRemoteEvent) = - ( - FfiConverterTypePublicKey.allocationSize(value.`from`) + - FfiConverterTypeEntry.allocationSize(value.`entry`) + - FfiConverterTypeContentStatus.allocationSize(value.`contentStatus`) - ) - - override fun write( - value: InsertRemoteEvent, - buf: ByteBuffer, - ) { - FfiConverterTypePublicKey.write(value.`from`, buf) - FfiConverterTypeEntry.write(value.`entry`, buf) - FfiConverterTypeContentStatus.write(value.`contentStatus`, buf) - } -} - -/** - * The latency and type of the control message - */ -data class LatencyAndControlMsg( - /** - * The latency of the control message - */ - var `latency`: java.time.Duration, - /** - * The type of control message, represented as a string - */ - var `controlMsg`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeLatencyAndControlMsg : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): LatencyAndControlMsg = - LatencyAndControlMsg( - FfiConverterDuration.read(buf), - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: LatencyAndControlMsg) = - ( - FfiConverterDuration.allocationSize(value.`latency`) + - FfiConverterString.allocationSize(value.`controlMsg`) - ) - - override fun write( - value: LatencyAndControlMsg, - buf: ByteBuffer, - ) { - FfiConverterDuration.write(value.`latency`, buf) - FfiConverterString.write(value.`controlMsg`, buf) - } -} - -/** - * `LinkAndName` includes a name and a hash for a blob in a collection - */ -data class LinkAndName( - /** - * The name associated with this [`Hash`] - */ - var `name`: kotlin.String, - /** - * The [`Hash`] of the blob - */ - var `link`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`name`) - - Disposable.destroy(this.`link`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeLinkAndName : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): LinkAndName = - LinkAndName( - FfiConverterString.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: LinkAndName) = - ( - FfiConverterString.allocationSize(value.`name`) + - FfiConverterTypeHash.allocationSize(value.`link`) - ) - - override fun write( - value: LinkAndName, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`name`, buf) - FfiConverterTypeHash.write(value.`link`, buf) - } -} - -/** - * The actual content of a gossip message. - */ -data class MessageContent( - /** - * The content of the message - */ - var `content`: kotlin.ByteArray, - /** - * The node that delivered the message. This is not the same as the original author. - */ - var `deliveredFrom`: kotlin.String, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeMessageContent : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): MessageContent = - MessageContent( - FfiConverterByteArray.read(buf), - FfiConverterString.read(buf), - ) - - override fun allocationSize(value: MessageContent) = - ( - FfiConverterByteArray.allocationSize(value.`content`) + - FfiConverterString.allocationSize(value.`deliveredFrom`) - ) - - override fun write( - value: MessageContent, - buf: ByteBuffer, - ) { - FfiConverterByteArray.write(value.`content`, buf) - FfiConverterString.write(value.`deliveredFrom`, buf) - } -} - -/** - * The namespace id and CapabilityKind (read/write) of the doc - */ -data class NamespaceAndCapability( - /** - * The namespace id of the doc - */ - var `namespace`: kotlin.String, - /** - * The capability you have for the doc (read/write) - */ - var `capability`: CapabilityKind, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNamespaceAndCapability : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): NamespaceAndCapability = - NamespaceAndCapability( - FfiConverterString.read(buf), - FfiConverterTypeCapabilityKind.read(buf), - ) - - override fun allocationSize(value: NamespaceAndCapability) = - ( - FfiConverterString.allocationSize(value.`namespace`) + - FfiConverterTypeCapabilityKind.allocationSize(value.`capability`) - ) - - override fun write( - value: NamespaceAndCapability, - buf: ByteBuffer, - ) { - FfiConverterString.write(value.`namespace`, buf) - FfiConverterTypeCapabilityKind.write(value.`capability`, buf) - } -} - -/** - * Options passed to [`IrohNode.new`]. Controls the behaviour of an iroh node. - */ -data class NodeOptions( - /** - * How frequently the blob store should clean up unreferenced blobs, in milliseconds. - * Set to 0 to disable gc - */ - var `gcIntervalMillis`: kotlin.ULong? = null, - /** - * Provide a callback to hook into events when the blobs component adds and provides blobs. - */ - var `blobEvents`: BlobProvideEventCallback? = null, - /** - * Should docs be enabled? Defaults to `false`. - */ - var `enableDocs`: kotlin.Boolean = false, - /** - * Overwrites the default IPv4 address to bind to - */ - var `ipv4Addr`: kotlin.String? = null, - /** - * Overwrites the default IPv6 address to bind to - */ - var `ipv6Addr`: kotlin.String? = null, - /** - * Configure the node discovery. Defaults to the default set of config - */ - var `nodeDiscovery`: NodeDiscoveryConfig? = null, - /** - * Provide a specific secret key, identifying this node. Must be 32 bytes long. - */ - var `secretKey`: kotlin.ByteArray? = null, - var `protocols`: Map? = null, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`gcIntervalMillis`) - - Disposable.destroy(this.`blobEvents`) - - Disposable.destroy(this.`enableDocs`) - - Disposable.destroy(this.`ipv4Addr`) - - Disposable.destroy(this.`ipv6Addr`) - - Disposable.destroy(this.`nodeDiscovery`) - - Disposable.destroy(this.`secretKey`) - - Disposable.destroy(this.`protocols`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNodeOptions : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): NodeOptions = - NodeOptions( - FfiConverterOptionalULong.read(buf), - FfiConverterOptionalTypeBlobProvideEventCallback.read(buf), - FfiConverterBoolean.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterOptionalTypeNodeDiscoveryConfig.read(buf), - FfiConverterOptionalByteArray.read(buf), - FfiConverterOptionalMapByteArrayTypeProtocolCreator.read(buf), - ) - - override fun allocationSize(value: NodeOptions) = - ( - FfiConverterOptionalULong.allocationSize(value.`gcIntervalMillis`) + - FfiConverterOptionalTypeBlobProvideEventCallback.allocationSize(value.`blobEvents`) + - FfiConverterBoolean.allocationSize(value.`enableDocs`) + - FfiConverterOptionalString.allocationSize(value.`ipv4Addr`) + - FfiConverterOptionalString.allocationSize(value.`ipv6Addr`) + - FfiConverterOptionalTypeNodeDiscoveryConfig.allocationSize(value.`nodeDiscovery`) + - FfiConverterOptionalByteArray.allocationSize(value.`secretKey`) + - FfiConverterOptionalMapByteArrayTypeProtocolCreator.allocationSize(value.`protocols`) - ) - - override fun write( - value: NodeOptions, - buf: ByteBuffer, - ) { - FfiConverterOptionalULong.write(value.`gcIntervalMillis`, buf) - FfiConverterOptionalTypeBlobProvideEventCallback.write(value.`blobEvents`, buf) - FfiConverterBoolean.write(value.`enableDocs`, buf) - FfiConverterOptionalString.write(value.`ipv4Addr`, buf) - FfiConverterOptionalString.write(value.`ipv6Addr`, buf) - FfiConverterOptionalTypeNodeDiscoveryConfig.write(value.`nodeDiscovery`, buf) - FfiConverterOptionalByteArray.write(value.`secretKey`, buf) - FfiConverterOptionalMapByteArrayTypeProtocolCreator.write(value.`protocols`, buf) - } -} - -/** - * The state for an open replica. - */ -data class OpenState( - /** - * Whether to accept sync requests for this replica. - */ - var `sync`: kotlin.Boolean, - /** - * How many event subscriptions are open - */ - var `subscribers`: kotlin.ULong, - /** - * By how many handles the replica is currently held open - */ - var `handles`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeOpenState : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): OpenState = - OpenState( - FfiConverterBoolean.read(buf), - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: OpenState) = - ( - FfiConverterBoolean.allocationSize(value.`sync`) + - FfiConverterULong.allocationSize(value.`subscribers`) + - FfiConverterULong.allocationSize(value.`handles`) - ) - - override fun write( - value: OpenState, - buf: ByteBuffer, - ) { - FfiConverterBoolean.write(value.`sync`, buf) - FfiConverterULong.write(value.`subscribers`, buf) - FfiConverterULong.write(value.`handles`, buf) - } -} - -/** - * Options for sorting and pagination for using [`Query`]s. - */ -data class QueryOptions( - /** - * Sort by author or key first. - * - * Default is [`SortBy::AuthorKey`], so sorting first by author and then by key. - */ - var `sortBy`: SortBy, - /** - * Direction by which to sort the entries - * - * Default is [`SortDirection::Asc`] - */ - var `direction`: SortDirection, - /** - * Offset - */ - var `offset`: kotlin.ULong, - /** - * Limit to limit the pagination. - * - * When the limit is 0, the limit does not exist. - */ - var `limit`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeQueryOptions : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): QueryOptions = - QueryOptions( - FfiConverterTypeSortBy.read(buf), - FfiConverterTypeSortDirection.read(buf), - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: QueryOptions) = - ( - FfiConverterTypeSortBy.allocationSize(value.`sortBy`) + - FfiConverterTypeSortDirection.allocationSize(value.`direction`) + - FfiConverterULong.allocationSize(value.`offset`) + - FfiConverterULong.allocationSize(value.`limit`) - ) - - override fun write( - value: QueryOptions, - buf: ByteBuffer, - ) { - FfiConverterTypeSortBy.write(value.`sortBy`, buf) - FfiConverterTypeSortDirection.write(value.`direction`, buf) - FfiConverterULong.write(value.`offset`, buf) - FfiConverterULong.write(value.`limit`, buf) - } -} - -/** - * Information about a remote node - */ -data class RemoteInfo( - /** - * The node identifier of the endpoint. Also a public key. - */ - var `nodeId`: PublicKey, - /** - * Relay url, if available. - */ - var `relayUrl`: kotlin.String?, - /** - * List of addresses at which this node might be reachable, plus any latency information we - * have about that address and the last time the address was used. - */ - var `addrs`: List, - /** - * The type of connection we have to the peer, either direct or over relay. - */ - var `connType`: ConnectionType, - /** - * The latency of the `conn_type`. - */ - var `latency`: java.time.Duration?, - /** - * Duration since the last time this peer was used. - */ - var `lastUsed`: java.time.Duration?, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`nodeId`) - - Disposable.destroy(this.`relayUrl`) - - Disposable.destroy(this.`addrs`) - - Disposable.destroy(this.`connType`) - - Disposable.destroy(this.`latency`) - - Disposable.destroy(this.`lastUsed`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeRemoteInfo : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteInfo = - RemoteInfo( - FfiConverterTypePublicKey.read(buf), - FfiConverterOptionalString.read(buf), - FfiConverterSequenceTypeDirectAddrInfo.read(buf), - FfiConverterTypeConnectionType.read(buf), - FfiConverterOptionalDuration.read(buf), - FfiConverterOptionalDuration.read(buf), - ) - - override fun allocationSize(value: RemoteInfo) = - ( - FfiConverterTypePublicKey.allocationSize(value.`nodeId`) + - FfiConverterOptionalString.allocationSize(value.`relayUrl`) + - FfiConverterSequenceTypeDirectAddrInfo.allocationSize(value.`addrs`) + - FfiConverterTypeConnectionType.allocationSize(value.`connType`) + - FfiConverterOptionalDuration.allocationSize(value.`latency`) + - FfiConverterOptionalDuration.allocationSize(value.`lastUsed`) - ) - - override fun write( - value: RemoteInfo, - buf: ByteBuffer, - ) { - FfiConverterTypePublicKey.write(value.`nodeId`, buf) - FfiConverterOptionalString.write(value.`relayUrl`, buf) - FfiConverterSequenceTypeDirectAddrInfo.write(value.`addrs`, buf) - FfiConverterTypeConnectionType.write(value.`connType`, buf) - FfiConverterOptionalDuration.write(value.`latency`, buf) - FfiConverterOptionalDuration.write(value.`lastUsed`, buf) - } -} - -/** - * Outcome of a sync operation - */ -data class SyncEvent( - /** - * Peer we synced with - */ - var `peer`: PublicKey, - /** - * Origin of the sync exchange - */ - var `origin`: Origin, - /** - * Timestamp when the sync finished - */ - var `finished`: java.time.Instant, - /** - * Timestamp when the sync started - */ - var `started`: java.time.Instant, - /** - * Result of the sync operation. `None` if successfull. - */ - var `result`: kotlin.String?, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`peer`) - - Disposable.destroy(this.`origin`) - - Disposable.destroy(this.`finished`) - - Disposable.destroy(this.`started`) - - Disposable.destroy(this.`result`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeSyncEvent : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): SyncEvent = - SyncEvent( - FfiConverterTypePublicKey.read(buf), - FfiConverterTypeOrigin.read(buf), - FfiConverterTimestamp.read(buf), - FfiConverterTimestamp.read(buf), - FfiConverterOptionalString.read(buf), - ) - - override fun allocationSize(value: SyncEvent) = - ( - FfiConverterTypePublicKey.allocationSize(value.`peer`) + - FfiConverterTypeOrigin.allocationSize(value.`origin`) + - FfiConverterTimestamp.allocationSize(value.`finished`) + - FfiConverterTimestamp.allocationSize(value.`started`) + - FfiConverterOptionalString.allocationSize(value.`result`) - ) - - override fun write( - value: SyncEvent, - buf: ByteBuffer, - ) { - FfiConverterTypePublicKey.write(value.`peer`, buf) - FfiConverterTypeOrigin.write(value.`origin`, buf) - FfiConverterTimestamp.write(value.`finished`, buf) - FfiConverterTimestamp.write(value.`started`, buf) - FfiConverterOptionalString.write(value.`result`, buf) - } -} - -/** - * A response to a list collections request - */ -data class TagInfo( - /** - * The tag - */ - var `name`: kotlin.ByteArray, - /** - * The format of the associated blob - */ - var `format`: BlobFormat, - /** - * The hash of the associated blob - */ - var `hash`: Hash, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`name`) - - Disposable.destroy(this.`format`) - - Disposable.destroy(this.`hash`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTagInfo : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TagInfo = - TagInfo( - FfiConverterByteArray.read(buf), - FfiConverterTypeBlobFormat.read(buf), - FfiConverterTypeHash.read(buf), - ) - - override fun allocationSize(value: TagInfo) = - ( - FfiConverterByteArray.allocationSize(value.`name`) + - FfiConverterTypeBlobFormat.allocationSize(value.`format`) + - FfiConverterTypeHash.allocationSize(value.`hash`) - ) - - override fun write( - value: TagInfo, - buf: ByteBuffer, - ) { - FfiConverterByteArray.write(value.`name`, buf) - FfiConverterTypeBlobFormat.write(value.`format`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - } -} - -/** - * An BlobProvide event indicating a new tagged blob or collection was added - */ -data class TaggedBlobAdded( - /** - * The hash of the added data - */ - var `hash`: Hash, - /** - * The format of the added data - */ - var `format`: BlobFormat, - /** - * The tag of the added data - */ - var `tag`: kotlin.ByteArray, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`format`) - - Disposable.destroy(this.`tag`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTaggedBlobAdded : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TaggedBlobAdded = - TaggedBlobAdded( - FfiConverterTypeHash.read(buf), - FfiConverterTypeBlobFormat.read(buf), - FfiConverterByteArray.read(buf), - ) - - override fun allocationSize(value: TaggedBlobAdded) = - ( - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterTypeBlobFormat.allocationSize(value.`format`) + - FfiConverterByteArray.allocationSize(value.`tag`) - ) - - override fun write( - value: TaggedBlobAdded, - buf: ByteBuffer, - ) { - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterTypeBlobFormat.write(value.`format`, buf) - FfiConverterByteArray.write(value.`tag`, buf) - } -} - -/** - * A request was aborted because the client disconnected. - */ -data class TransferAborted( - /** - * The quic connection id. - */ - var `connectionId`: kotlin.ULong, - /** - * An identifier uniquely identifying this request. - */ - var `requestId`: kotlin.ULong, - /** - * statistics about the transfer. This is None if the transfer - * was aborted before any data was sent. - */ - var `stats`: TransferStats?, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTransferAborted : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferAborted = - TransferAborted( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterOptionalTypeTransferStats.read(buf), - ) - - override fun allocationSize(value: TransferAborted) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) + - FfiConverterULong.allocationSize(value.`requestId`) + - FfiConverterOptionalTypeTransferStats.allocationSize(value.`stats`) - ) - - override fun write( - value: TransferAborted, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - FfiConverterULong.write(value.`requestId`, buf) - FfiConverterOptionalTypeTransferStats.write(value.`stats`, buf) - } -} - -/** - * A blob in a sequence was transferred. - */ -data class TransferBlobCompleted( - /** - * An unique connection id. - */ - var `connectionId`: kotlin.ULong, - /** - * An identifier uniquely identifying this transfer request. - */ - var `requestId`: kotlin.ULong, - /** - * The hash of the blob - */ - var `hash`: Hash, - /** - * The index of the blob in the sequence. - */ - var `index`: kotlin.ULong, - /** - * The size of the blob transferred. - */ - var `size`: kotlin.ULong, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`connectionId`) - - Disposable.destroy(this.`requestId`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`index`) - - Disposable.destroy(this.`size`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTransferBlobCompleted : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferBlobCompleted = - TransferBlobCompleted( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: TransferBlobCompleted) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) + - FfiConverterULong.allocationSize(value.`requestId`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterULong.allocationSize(value.`index`) + - FfiConverterULong.allocationSize(value.`size`) - ) - - override fun write( - value: TransferBlobCompleted, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - FfiConverterULong.write(value.`requestId`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterULong.write(value.`index`, buf) - FfiConverterULong.write(value.`size`, buf) - } -} - -/** - * A request was completed and the data was sent to the client. - */ -data class TransferCompleted( - /** - * An unique connection id. - */ - var `connectionId`: kotlin.ULong, - /** - * An identifier uniquely identifying this transfer request. - */ - var `requestId`: kotlin.ULong, - /** - * statistics about the transfer - */ - var `stats`: TransferStats, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTransferCompleted : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferCompleted = - TransferCompleted( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeTransferStats.read(buf), - ) - - override fun allocationSize(value: TransferCompleted) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) + - FfiConverterULong.allocationSize(value.`requestId`) + - FfiConverterTypeTransferStats.allocationSize(value.`stats`) - ) - - override fun write( - value: TransferCompleted, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - FfiConverterULong.write(value.`requestId`, buf) - FfiConverterTypeTransferStats.write(value.`stats`, buf) - } -} - -/** - * A sequence of hashes has been found and is being transferred. - */ -data class TransferHashSeqStarted( - /** - * An unique connection id. - */ - var `connectionId`: kotlin.ULong, - /** - * An identifier uniquely identifying this transfer request. - */ - var `requestId`: kotlin.ULong, - /** - * The number of blobs in the sequence. - */ - var `numBlobs`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTransferHashSeqStarted : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferHashSeqStarted = - TransferHashSeqStarted( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: TransferHashSeqStarted) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) + - FfiConverterULong.allocationSize(value.`requestId`) + - FfiConverterULong.allocationSize(value.`numBlobs`) - ) - - override fun write( - value: TransferHashSeqStarted, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - FfiConverterULong.write(value.`requestId`, buf) - FfiConverterULong.write(value.`numBlobs`, buf) - } -} - -/** - * A chunk of a blob was transferred. - * - * These events will be sent with try_send, so you can not assume that you - * will receive all of them. - */ -data class TransferProgress( - /** - * An unique connection id. - */ - var `connectionId`: kotlin.ULong, - /** - * An identifier uniquely identifying this transfer request. - */ - var `requestId`: kotlin.ULong, - /** - * The hash for which we are transferring data. - */ - var `hash`: Hash, - /** - * Offset up to which we have transferred data. - */ - var `endOffset`: kotlin.ULong, -) : Disposable { - @Suppress("UNNECESSARY_SAFE_CALL") // codegen is much simpler if we unconditionally emit safe calls here - override fun destroy() { - Disposable.destroy(this.`connectionId`) - - Disposable.destroy(this.`requestId`) - - Disposable.destroy(this.`hash`) - - Disposable.destroy(this.`endOffset`) - } - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTransferProgress : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferProgress = - TransferProgress( - FfiConverterULong.read(buf), - FfiConverterULong.read(buf), - FfiConverterTypeHash.read(buf), - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: TransferProgress) = - ( - FfiConverterULong.allocationSize(value.`connectionId`) + - FfiConverterULong.allocationSize(value.`requestId`) + - FfiConverterTypeHash.allocationSize(value.`hash`) + - FfiConverterULong.allocationSize(value.`endOffset`) - ) - - override fun write( - value: TransferProgress, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`connectionId`, buf) - FfiConverterULong.write(value.`requestId`, buf) - FfiConverterTypeHash.write(value.`hash`, buf) - FfiConverterULong.write(value.`endOffset`, buf) - } -} - -/** - * The stats for a transfer of a collection or blob. - */ -data class TransferStats( - /** - * The total duration of the transfer in milliseconds - */ - var `duration`: kotlin.ULong, -) { - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeTransferStats : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferStats = - TransferStats( - FfiConverterULong.read(buf), - ) - - override fun allocationSize(value: TransferStats) = - ( - FfiConverterULong.allocationSize(value.`duration`) - ) - - override fun write( - value: TransferStats, - buf: ByteBuffer, - ) { - FfiConverterULong.write(value.`duration`, buf) - } -} - -/** - * The different types of AddProgress events - */ - -enum class AddProgressType { - /** - * An item was found with name `name`, from now on referred to via `id` - */ - FOUND, - - /** - * We got progress ingesting item `id`. - */ - PROGRESS, - - /** - * We are done with `id`, and the hash is `hash`. - */ - DONE, - - /** - * We are done with the whole operation. - */ - ALL_DONE, - - /** - * We got an error and need to abort. - * - * This will be the last message in the stream. - */ - ABORT, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddProgressType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - AddProgressType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: AddProgressType) = 4UL - - override fun write( - value: AddProgressType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * Options when creating a ticket - */ - -enum class AddrInfoOptions { - /** - * Only the Node ID is added. - * - * This usually means that iroh-dns discovery is used to find address information. - */ - ID, - - /** - * Include both the relay URL and the direct addresses. - */ - RELAY_AND_ADDRESSES, - - /** - * Only include the relay URL. - */ - RELAY, - - /** - * Only include the direct addresses. - */ - ADDRESSES, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeAddrInfoOptions : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - AddrInfoOptions.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: AddrInfoOptions) = 4UL - - override fun write( - value: AddrInfoOptions, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The expected format of a hash being exported. - */ - -enum class BlobExportFormat { - /** - * The hash refers to any blob and will be exported to a single file. - */ - BLOB, - - /** - * The hash refers to a [`crate::format::collection::Collection`] blob - * and all children of the collection shall be exported to one file per child. - * - * If the blob can be parsed as a [`BlobFormat::HashSeq`], and the first child contains - * collection metadata, all other children of the collection will be exported to - * a file each, with their collection name treated as a relative path to the export - * destination path. - * - * If the blob cannot be parsed as a collection, the operation will fail. - */ - COLLECTION, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobExportFormat : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - BlobExportFormat.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: BlobExportFormat) = 4UL - - override fun write( - value: BlobExportFormat, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The export mode describes how files will be exported. - * - * This is a hint to the import trait method. For some implementations, this - * does not make any sense. E.g. an in memory implementation will always have - * to copy the file into memory. Also, a disk based implementation might choose - * to copy small files even if the mode is `Reference`. - */ - -enum class BlobExportMode { - /** - * This mode will copy the file to the target directory. - * - * This is the safe default because the file can not be accidentally modified - * after it has been exported. - */ - COPY, - - /** - * This mode will try to move the file to the target directory and then reference it from - * the database. - * - * This has a large performance and storage benefit, but it is less safe since - * the file might be modified in the target directory after it has been exported. - * - * Stores are allowed to ignore this mode and always copy the file, e.g. - * if the file is very small or if the store does not support referencing files. - */ - TRY_REFERENCE, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobExportMode : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - BlobExportMode.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: BlobExportMode) = 4UL - - override fun write( - value: BlobExportMode, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * A format identifier - */ - -enum class BlobFormat { - /** - * Raw blob - */ - RAW, - - /** - * A sequence of BLAKE3 hashes - */ - HASH_SEQ, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobFormat : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - BlobFormat.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: BlobFormat) = 4UL - - override fun write( - value: BlobFormat, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The different types of BlobProvide events - */ - -enum class BlobProvideEventType { - /** - * A new collection or tagged blob has been added - */ - TAGGED_BLOB_ADDED, - - /** - * A new client connected to the node. - */ - CLIENT_CONNECTED, - - /** - * A request was received from a client. - */ - GET_REQUEST_RECEIVED, - - /** - * A sequence of hashes has been found and is being transferred. - */ - TRANSFER_HASH_SEQ_STARTED, - - /** - * A chunk of a blob was transferred. - * - * it is not safe to assume all progress events will be sent - */ - TRANSFER_PROGRESS, - - /** - * A blob in a sequence was transferred. - */ - TRANSFER_BLOB_COMPLETED, - - /** - * A request was completed and the data was sent to the client. - */ - TRANSFER_COMPLETED, - - /** - * A request was aborted because the client disconnected. - */ - TRANSFER_ABORTED, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeBlobProvideEventType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - BlobProvideEventType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: BlobProvideEventType) = 4UL - - override fun write( - value: BlobProvideEventType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -sealed class CallbackException : kotlin.Exception() { - class Exception : CallbackException() { - override val message - get() = "" - } - - companion object ErrorHandler : UniffiRustCallStatusErrorHandler { - override fun lift(error_buf: RustBuffer.ByValue): CallbackException = FfiConverterTypeCallbackError.lift(error_buf) - } -} - -/** - * @suppress - */ -public object FfiConverterTypeCallbackError : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): CallbackException = - when (buf.getInt()) { - 1 -> CallbackException.Exception() - else -> throw RuntimeException("invalid error enum value, something is very wrong!!") - } - - override fun allocationSize(value: CallbackException): ULong = - when (value) { - is CallbackException.Exception -> ( - // Add the size for the Int that specifies the variant plus the size needed for all fields - 4UL - ) - } - - override fun write( - value: CallbackException, - buf: ByteBuffer, - ) { - when (value) { - is CallbackException.Exception -> { - buf.putInt(1) - Unit - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } -} - -enum class CapabilityKind { - /** - * A writable replica. - */ - WRITE, - - /** - * A readable replica. - */ - READ, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeCapabilityKind : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - CapabilityKind.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: CapabilityKind) = 4UL - - override fun write( - value: CapabilityKind, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The type of the connection - */ - -enum class ConnType { - /** - * Indicates you have a UDP connection. - */ - DIRECT, - - /** - * Indicates you have a relayed connection. - */ - RELAY, - - /** - * Indicates you have an unverified UDP connection, and a relay connection for backup. - */ - MIXED, - - /** - * Indicates you have no proof of connection. - */ - NONE, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeConnType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - ConnType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: ConnType) = 4UL - - override fun write( - value: ConnType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * Whether the content status is available on a node. - */ - -enum class ContentStatus { - /** - * The content is completely available. - */ - COMPLETE, - - /** - * The content is partially available. - */ - INCOMPLETE, - - /** - * The content is missing. - */ - MISSING, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeContentStatus : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - ContentStatus.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: ContentStatus) = 4UL - - override fun write( - value: ContentStatus, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The type of `DocExportProgress` event - */ - -enum class DocExportProgressType { - /** - * An item was found with name `name`, from now on referred to via `id` - */ - FOUND, - - /** - * We got progress exporting item `id`. - */ - PROGRESS, - - /** - * We finished exporting a blob with `id` - */ - DONE, - - /** - * We are done writing the entry to the filesystem - */ - ALL_DONE, - - /** - * We got an error and need to abort. - * - * This will be the last message in the stream. - */ - ABORT, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocExportProgressType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - DocExportProgressType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: DocExportProgressType) = 4UL - - override fun write( - value: DocExportProgressType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The type of `DocImportProgress` event - */ - -enum class DocImportProgressType { - /** - * An item was found with name `name`, from now on referred to via `id` - */ - FOUND, - - /** - * We got progress ingesting item `id`. - */ - PROGRESS, - - /** - * We are done ingesting `id`, and the hash is `hash`. - */ - INGEST_DONE, - - /** - * We are done with the whole operation. - */ - ALL_DONE, - - /** - * We got an error and need to abort. - * - * This will be the last message in the stream. - */ - ABORT, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDocImportProgressType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - DocImportProgressType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: DocImportProgressType) = 4UL - - override fun write( - value: DocImportProgressType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The different types of DownloadProgress events - */ - -enum class DownloadProgressType { - INITIAL_STATE, - FOUND_LOCAL, - CONNECTED, - FOUND, - FOUND_HASH_SEQ, - PROGRESS, - DONE, - ALL_DONE, - ABORT, - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeDownloadProgressType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - DownloadProgressType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: DownloadProgressType) = 4UL - - override fun write( - value: DownloadProgressType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The type of events that can be emitted during the live sync progress - */ - -enum class LiveEventType { - /** - * A local insertion. - */ - INSERT_LOCAL, - - /** - * Received a remote insert. - */ - INSERT_REMOTE, - - /** - * The content of an entry was downloaded and is now available at the local node - */ - CONTENT_READY, - - /** - * We have a new neighbor in the swarm. - */ - NEIGHBOR_UP, - - /** - * We lost a neighbor in the swarm. - */ - NEIGHBOR_DOWN, - - /** - * A set-reconciliation sync finished. - */ - SYNC_FINISHED, - - /** - * All pending content is now ready. - * - * This event signals that all queued content downloads from the last sync run have either - * completed or failed. - * - * It will only be emitted after a [`Self::SyncFinished`] event, never before. - * - * Receiving this event does not guarantee that all content in the document is available. If - * blobs failed to download, this event will still be emitted after all operations completed. - */ - PENDING_CONTENT_READY, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeLiveEventType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - LiveEventType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: LiveEventType) = 4UL - - override fun write( - value: LiveEventType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * The logging level. See the rust (log crate)[https://docs.rs/log] for more information. - */ - -enum class LogLevel { - TRACE, - DEBUG, - INFO, - WARN, - ERROR, - OFF, - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeLogLevel : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - LogLevel.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: LogLevel) = 4UL - - override fun write( - value: LogLevel, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -enum class MessageType { - NEIGHBOR_UP, - NEIGHBOR_DOWN, - RECEIVED, - JOINED, - LAGGED, - ERROR, - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeMessageType : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - MessageType.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: MessageType) = 4UL - - override fun write( - value: MessageType, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -enum class NodeDiscoveryConfig { - /** - * Use no node discovery mechanism. - */ - NONE, - - /** - * Use the default discovery mechanism. - * - * This uses two discovery services concurrently: - * - * - It publishes to a pkarr service operated by [number 0] which makes the information - * available via DNS in the `iroh.link` domain. - * - * - It uses an mDNS-like system to announce itself on the local network. - * - * # Usage during tests - * - * Note that the default changes when compiling with `cfg(test)` or the `test-utils` - * cargo feature from [iroh-net] is enabled. In this case only the Pkarr/DNS service - * is used, but on the `iroh.test` domain. This domain is not integrated with the - * global DNS network and thus node discovery is effectively disabled. To use node - * discovery in a test use the [`iroh_net::test_utils::DnsPkarrServer`] in the test and - * configure it here as a custom discovery mechanism ([`DiscoveryConfig::Custom`]). - * - * [number 0]: https://n0.computer - */ - DEFAULT, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeNodeDiscoveryConfig : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - NodeDiscoveryConfig.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: NodeDiscoveryConfig) = 4UL - - override fun write( - value: NodeDiscoveryConfig, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * Why we performed a sync exchange - */ -sealed class Origin { - /** - * public, use a unit variant - */ - data class Connect( - val `reason`: SyncReason, - ) : Origin() { - companion object - } - - /** - * A peer connected to us and we accepted the exchange - */ - object Accept : Origin() - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeOrigin : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): Origin = - when (buf.getInt()) { - 1 -> - Origin.Connect( - FfiConverterTypeSyncReason.read(buf), - ) - 2 -> Origin.Accept - else -> throw RuntimeException("invalid enum value, something is very wrong!!") - } - - override fun allocationSize(value: Origin) = - when (value) { - is Origin.Connect -> { - // Add the size for the Int that specifies the variant plus the size needed for all fields - ( - 4UL + - FfiConverterTypeSyncReason.allocationSize(value.`reason`) - ) - } - is Origin.Accept -> { - // Add the size for the Int that specifies the variant plus the size needed for all fields - ( - 4UL - ) - } - } - - override fun write( - value: Origin, - buf: ByteBuffer, - ) { - when (value) { - is Origin.Connect -> { - buf.putInt(1) - FfiConverterTypeSyncReason.write(value.`reason`, buf) - Unit - } - is Origin.Accept -> { - buf.putInt(2) - Unit - } - }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } - } -} - -/** - * Intended capability for document share tickets - */ - -enum class ShareMode { - /** - * Read-only access - */ - READ, - - /** - * Write access - */ - WRITE, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeShareMode : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - ShareMode.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: ShareMode) = 4UL - - override fun write( - value: ShareMode, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * d Fields by which the query can be sorted - */ - -enum class SortBy { - /** - * Sort by key, then author. - */ - KEY_AUTHOR, - - /** - * Sort by author, then key. - */ - AUTHOR_KEY, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeSortBy : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - SortBy.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: SortBy) = 4UL - - override fun write( - value: SortBy, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * Sort direction - */ - -enum class SortDirection { - /** - * Sort ascending - */ - ASC, - - /** - * Sort descending - */ - DESC, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeSortDirection : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - SortDirection.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: SortDirection) = 4UL - - override fun write( - value: SortDirection, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * Why we started a sync request - */ - -enum class SyncReason { - /** - * Direct join request via API - */ - DIRECT_JOIN, - - /** - * Peer showed up as new neighbor in the gossip swarm - */ - NEW_NEIGHBOR, - - /** - * We synced after receiving a sync report that indicated news for us - */ - SYNC_REPORT, - - /** - * We received a sync report while a sync was running, so run again afterwars - */ - RESYNC, - - ; - - companion object -} - -/** - * @suppress - */ -public object FfiConverterTypeSyncReason : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer) = - try { - SyncReason.values()[buf.getInt() - 1] - } catch (e: IndexOutOfBoundsException) { - throw RuntimeException("invalid enum value, something is very wrong!!", e) - } - - override fun allocationSize(value: SyncReason) = 4UL - - override fun write( - value: SyncReason, - buf: ByteBuffer, - ) { - buf.putInt(value.ordinal + 1) - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalULong : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ULong? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterULong.read(buf) - } - - override fun allocationSize(value: kotlin.ULong?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterULong.allocationSize(value) - } - } - - override fun write( - value: kotlin.ULong?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterULong.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalString : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.String? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterString.read(buf) - } - - override fun allocationSize(value: kotlin.String?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterString.allocationSize(value) - } - } - - override fun write( - value: kotlin.String?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterString.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalByteArray : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): kotlin.ByteArray? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterByteArray.read(buf) - } - - override fun allocationSize(value: kotlin.ByteArray?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterByteArray.allocationSize(value) - } - } - - override fun write( - value: kotlin.ByteArray?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterByteArray.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalDuration : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): java.time.Duration? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterDuration.read(buf) - } - - override fun allocationSize(value: java.time.Duration?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterDuration.allocationSize(value) - } - } - - override fun write( - value: java.time.Duration?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterDuration.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeBlobProvideEventCallback : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): BlobProvideEventCallback? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeBlobProvideEventCallback.read(buf) - } - - override fun allocationSize(value: BlobProvideEventCallback?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeBlobProvideEventCallback.allocationSize(value) - } - } - - override fun write( - value: BlobProvideEventCallback?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeBlobProvideEventCallback.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeDoc : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): Doc? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeDoc.read(buf) - } - - override fun allocationSize(value: Doc?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeDoc.allocationSize(value) - } - } - - override fun write( - value: Doc?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeDoc.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeDocExportFileCallback : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocExportFileCallback? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeDocExportFileCallback.read(buf) - } - - override fun allocationSize(value: DocExportFileCallback?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeDocExportFileCallback.allocationSize(value) - } - } - - override fun write( - value: DocExportFileCallback?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeDocExportFileCallback.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeDocImportFileCallback : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): DocImportFileCallback? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeDocImportFileCallback.read(buf) - } - - override fun allocationSize(value: DocImportFileCallback?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeDocImportFileCallback.allocationSize(value) - } - } - - override fun write( - value: DocImportFileCallback?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeDocImportFileCallback.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeEntry : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): Entry? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeEntry.read(buf) - } - - override fun allocationSize(value: Entry?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeEntry.allocationSize(value) - } - } - - override fun write( - value: Entry?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeEntry.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeLatencyAndControlMsg : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): LatencyAndControlMsg? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeLatencyAndControlMsg.read(buf) - } - - override fun allocationSize(value: LatencyAndControlMsg?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeLatencyAndControlMsg.allocationSize(value) - } - } - - override fun write( - value: LatencyAndControlMsg?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeLatencyAndControlMsg.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeQueryOptions : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): QueryOptions? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeQueryOptions.read(buf) - } - - override fun allocationSize(value: QueryOptions?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeQueryOptions.allocationSize(value) - } - } - - override fun write( - value: QueryOptions?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeQueryOptions.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeRemoteInfo : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): RemoteInfo? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeRemoteInfo.read(buf) - } - - override fun allocationSize(value: RemoteInfo?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeRemoteInfo.allocationSize(value) - } - } - - override fun write( - value: RemoteInfo?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeRemoteInfo.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeTransferStats : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): TransferStats? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeTransferStats.read(buf) - } - - override fun allocationSize(value: TransferStats?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeTransferStats.allocationSize(value) - } - } - - override fun write( - value: TransferStats?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeTransferStats.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalTypeNodeDiscoveryConfig : FfiConverterRustBuffer { - override fun read(buf: ByteBuffer): NodeDiscoveryConfig? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterTypeNodeDiscoveryConfig.read(buf) - } - - override fun allocationSize(value: NodeDiscoveryConfig?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterTypeNodeDiscoveryConfig.allocationSize(value) - } - } - - override fun write( - value: NodeDiscoveryConfig?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterTypeNodeDiscoveryConfig.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalSequenceByteArray : FfiConverterRustBuffer?> { - override fun read(buf: ByteBuffer): List? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterSequenceByteArray.read(buf) - } - - override fun allocationSize(value: List?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterSequenceByteArray.allocationSize(value) - } - } - - override fun write( - value: List?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterSequenceByteArray.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterOptionalMapByteArrayTypeProtocolCreator : FfiConverterRustBuffer?> { - override fun read(buf: ByteBuffer): Map? { - if (buf.get().toInt() == 0) { - return null - } - return FfiConverterMapByteArrayTypeProtocolCreator.read(buf) - } - - override fun allocationSize(value: Map?): ULong { - if (value == null) { - return 1UL - } else { - return 1UL + FfiConverterMapByteArrayTypeProtocolCreator.allocationSize(value) - } - } - - override fun write( - value: Map?, - buf: ByteBuffer, - ) { - if (value == null) { - buf.put(0) - } else { - buf.put(1) - FfiConverterMapByteArrayTypeProtocolCreator.write(value, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceString : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterString.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterString.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterString.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceByteArray : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterByteArray.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterByteArray.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterByteArray.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeAuthorId : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeAuthorId.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeAuthorId.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeAuthorId.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeDirectAddrInfo : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeDirectAddrInfo.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeDirectAddrInfo.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeDirectAddrInfo.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeEntry : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeEntry.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeEntry.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeEntry.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeFilterKind : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeFilterKind.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeFilterKind.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeFilterKind.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeHash : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeHash.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeHash.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeHash.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeNodeAddr : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeNodeAddr.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeNodeAddr.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeNodeAddr.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeCollectionInfo : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeCollectionInfo.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeCollectionInfo.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeCollectionInfo.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeIncompleteBlobInfo : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeIncompleteBlobInfo.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeIncompleteBlobInfo.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeIncompleteBlobInfo.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeLinkAndName : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeLinkAndName.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeLinkAndName.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeLinkAndName.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeNamespaceAndCapability : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeNamespaceAndCapability.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeNamespaceAndCapability.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeNamespaceAndCapability.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeRemoteInfo : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeRemoteInfo.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeRemoteInfo.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeRemoteInfo.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterSequenceTypeTagInfo : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): List { - val len = buf.getInt() - return List(len) { - FfiConverterTypeTagInfo.read(buf) - } - } - - override fun allocationSize(value: List): ULong { - val sizeForLength = 4UL - val sizeForItems = value.map { FfiConverterTypeTagInfo.allocationSize(it) }.sum() - return sizeForLength + sizeForItems - } - - override fun write( - value: List, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - value.iterator().forEach { - FfiConverterTypeTagInfo.write(it, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterMapStringTypeCounterStats : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): Map { - val len = buf.getInt() - return buildMap(len) { - repeat(len) { - val k = FfiConverterString.read(buf) - val v = FfiConverterTypeCounterStats.read(buf) - this[k] = v - } - } - } - - override fun allocationSize(value: Map): ULong { - val spaceForMapSize = 4UL - val spaceForChildren = - value - .map { (k, v) -> - FfiConverterString.allocationSize(k) + - FfiConverterTypeCounterStats.allocationSize(v) - }.sum() - return spaceForMapSize + spaceForChildren - } - - override fun write( - value: Map, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - // The parens on `(k, v)` here ensure we're calling the right method, - // which is important for compatibility with older android devices. - // Ref https://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/ - value.forEach { (k, v) -> - FfiConverterString.write(k, buf) - FfiConverterTypeCounterStats.write(v, buf) - } - } -} - -/** - * @suppress - */ -public object FfiConverterMapByteArrayTypeProtocolCreator : FfiConverterRustBuffer> { - override fun read(buf: ByteBuffer): Map { - val len = buf.getInt() - return buildMap(len) { - repeat(len) { - val k = FfiConverterByteArray.read(buf) - val v = FfiConverterTypeProtocolCreator.read(buf) - this[k] = v - } - } - } - - override fun allocationSize(value: Map): ULong { - val spaceForMapSize = 4UL - val spaceForChildren = - value - .map { (k, v) -> - FfiConverterByteArray.allocationSize(k) + - FfiConverterTypeProtocolCreator.allocationSize(v) - }.sum() - return spaceForMapSize + spaceForChildren - } - - override fun write( - value: Map, - buf: ByteBuffer, - ) { - buf.putInt(value.size) - // The parens on `(k, v)` here ensure we're calling the right method, - // which is important for compatibility with older android devices. - // Ref https://blog.danlew.net/2017/03/16/kotlin-puzzler-whose-line-is-it-anyways/ - value.forEach { (k, v) -> - FfiConverterByteArray.write(k, buf) - FfiConverterTypeProtocolCreator.write(v, buf) - } - } -} - -/** - * Helper function that translates a key that was derived from the [`path_to_key`] function back - * into a path. - * - * If `prefix` exists, it will be stripped before converting back to a path - * If `root` exists, will add the root as a parent to the created path - * Removes any null byte that has been appened to the key - */ -@Throws(IrohException::class) -fun `keyToPath`( - `key`: kotlin.ByteArray, - `prefix`: kotlin.String?, - `root`: kotlin.String?, -): kotlin.String = - FfiConverterString.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_func_key_to_path( - FfiConverterByteArray.lower(`key`), - FfiConverterOptionalString.lower(`prefix`), - FfiConverterOptionalString.lower(`root`), - _status, - ) - }, - ) - -/** - * Helper function that creates a document key from a canonicalized path, removing the `root` and adding the `prefix`, if they exist - * - * Appends the null byte to the end of the key. - */ -@Throws(IrohException::class) -fun `pathToKey`( - `path`: kotlin.String, - `prefix`: kotlin.String?, - `root`: kotlin.String?, -): kotlin.ByteArray = - FfiConverterByteArray.lift( - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_func_path_to_key( - FfiConverterString.lower(`path`), - FfiConverterOptionalString.lower(`prefix`), - FfiConverterOptionalString.lower(`root`), - _status, - ) - }, - ) - -/** - * Set the logging level. - */ -fun `setLogLevel`(`level`: LogLevel) = - uniffiRustCall { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_func_set_log_level( - FfiConverterTypeLogLevel.lower(`level`), - _status, - ) - } - -/** - * Initialize the global metrics collection. - */ -@Throws(IrohException::class) -fun `startMetricsCollection`() = - uniffiRustCallWithError(IrohException) { _status -> - UniffiLib.INSTANCE.uniffi_iroh_ffi_fn_func_start_metrics_collection( - _status, - ) - } diff --git a/make_kotlin.sh b/make_kotlin.sh index 9b82aa6b..7a6151d4 100755 --- a/make_kotlin.sh +++ b/make_kotlin.sh @@ -22,13 +22,10 @@ case "$TEST_OS" in ;; esac -echo "building library" -cargo build --lib +cd kotlin -# UniFfi bindgen -echo "generating binding" -cargo run --bin uniffi-bindgen generate --language kotlin --out-dir kotlin/lib/src/main/kotlin/ --config uniffi.toml --library target/debug/$LIB_NAME.$LIB_EXTENSION +./gradlew generateNativeBindings # copy cdylib to outdir -mkdir -p kotlin/lib/src/main/resources/ -cp target/debug/$LIB_NAME.$LIB_EXTENSION kotlin/lib/src/main/resources/ +mkdir -p lib/src/main/resources/ +cp ../target/debug/$LIB_NAME.$LIB_EXTENSION lib/src/main/resources/