// 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 uniffi.pubkymobile; // 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.Library import com.sun.jna.IntegerType import com.sun.jna.Native import com.sun.jna.Pointer import com.sun.jna.Structure import com.sun.jna.Callback import com.sun.jna.ptr.* 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 java.util.concurrent.locks.ReentrantLock import kotlin.concurrent.withLock // 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. @Structure.FieldOrder("capacity", "len", "data") open class RustBuffer : Structure() { @JvmField var capacity: Int = 0 @JvmField var len: Int = 0 @JvmField var data: Pointer? = null class ByValue: RustBuffer(), Structure.ByValue class ByReference: RustBuffer(), Structure.ByReference companion object { internal fun alloc(size: Int = 0) = rustCall() { status -> _UniFFILib.INSTANCE.ffi_pubkymobile_rustbuffer_alloc(size, status) }.also { if(it.data == null) { throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") } } internal fun create(capacity: Int, len: Int, data: Pointer?): RustBuffer.ByValue { var buf = RustBuffer.ByValue() buf.capacity = capacity buf.len = len buf.data = data return buf } internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> _UniFFILib.INSTANCE.ffi_pubkymobile_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. */ 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.setInt(0, value.capacity) pointer.setInt(4, value.len) pointer.setPointer(8, value.data) } /** * Get a `RustBuffer.ByValue` from this reference. */ fun getValue(): RustBuffer.ByValue { val pointer = getPointer() val value = RustBuffer.ByValue() value.writeField("capacity", pointer.getInt(0)) value.writeField("len", pointer.getInt(4)) value.writeField("data", pointer.getPointer(8)) 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") 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. 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): Int // 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.toLong()).also { it.order(ByteOrder.BIG_ENDIAN) } write(value, bbuf) rbuf.writeField("len", bbuf.position()) 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 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. // Error runtime. @Structure.FieldOrder("code", "error_buf") internal open class RustCallStatus : Structure() { @JvmField var code: Byte = 0 @JvmField var error_buf: RustBuffer.ByValue = RustBuffer.ByValue() class ByValue: RustCallStatus(), Structure.ByValue fun isSuccess(): Boolean { return code == 0.toByte() } fun isError(): Boolean { return code == 1.toByte() } fun isPanic(): Boolean { return code == 2.toByte() } } class InternalException(message: String) : Exception(message) // Each top-level error class has a companion object that can lift the error from the call status's rust buffer interface CallStatusErrorHandler { 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 rustCallWithError(errorHandler: CallStatusErrorHandler, callback: (RustCallStatus) -> U): U { var status = RustCallStatus(); val return_value = callback(status) checkCallStatus(errorHandler, status) return return_value } // Check RustCallStatus and throw an error if the call wasn't successful private fun checkCallStatus(errorHandler: CallStatusErrorHandler, status: RustCallStatus) { 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") } } // CallStatusErrorHandler implementation for times when we don't expect a CALL_ERROR object NullCallStatusErrorHandler: CallStatusErrorHandler { 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 rustCall(callback: (RustCallStatus) -> U): U { return rustCallWithError(NullCallStatusErrorHandler, callback); } // IntegerType that matches Rust's `usize` / C's `size_t` public class USize(value: Long = 0) : IntegerType(Native.SIZE_T_SIZE, value, true) { // This is needed to fill in the gaps of IntegerType's implementation of Number for Kotlin. override fun toByte() = toInt().toByte() // Needed until https://youtrack.jetbrains.com/issue/KT-47902 is fixed. @Deprecated("`toInt().toChar()` is deprecated") override fun toChar() = toInt().toChar() override fun toShort() = toInt().toShort() fun writeToBuffer(buf: ByteBuffer) { // Make sure we always write usize integers using native byte-order, since they may be // casted to pointer values buf.order(ByteOrder.nativeOrder()) try { when (Native.SIZE_T_SIZE) { 4 -> buf.putInt(toInt()) 8 -> buf.putLong(toLong()) else -> throw RuntimeException("Invalid SIZE_T_SIZE: ${Native.SIZE_T_SIZE}") } } finally { buf.order(ByteOrder.BIG_ENDIAN) } } companion object { val size: Int get() = Native.SIZE_T_SIZE fun readFromBuffer(buf: ByteBuffer) : USize { // Make sure we always read usize integers using native byte-order, since they may be // casted from pointer values buf.order(ByteOrder.nativeOrder()) try { return when (Native.SIZE_T_SIZE) { 4 -> USize(buf.getInt().toLong()) 8 -> USize(buf.getLong()) else -> throw RuntimeException("Invalid SIZE_T_SIZE: ${Native.SIZE_T_SIZE}") } } finally { buf.order(ByteOrder.BIG_ENDIAN) } } } } // Map handles to objects // // This is used when the Rust code expects an opaque pointer to represent some foreign object. // Normally we would pass a pointer to the object, but JNA doesn't support getting a pointer from an // object reference , nor does it support leaking a reference to Rust. // // Instead, this class maps USize values to objects so that we can pass a pointer-sized type to // Rust when it needs an opaque pointer. // // TODO: refactor callbacks to use this class internal class UniFfiHandleMap { private val map = ConcurrentHashMap() // Use AtomicInteger for our counter, since we may be on a 32-bit system. 4 billion possible // values seems like enough. If somehow we generate 4 billion handles, then this will wrap // around back to zero and we can assume the first handle generated will have been dropped by // then. private val counter = java.util.concurrent.atomic.AtomicInteger(0) val size: Int get() = map.size fun insert(obj: T): USize { val handle = USize(counter.getAndAdd(1).toLong()) map.put(handle, obj) return handle } fun get(handle: USize): T? { return map.get(handle) } fun remove(handle: USize): T? { return map.remove(handle) } } // FFI type for Rust future continuations internal interface UniFffiRustFutureContinuationCallbackType : com.sun.jna.Callback { fun callback(continuationHandle: USize, pollResult: Short); } // 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 "pubkymobile" } private inline fun loadIndirect( componentName: String ): Lib { return Native.load(findLibraryName(componentName), Lib::class.java) } // 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<_UniFFILib>(componentName = "pubkymobile") .also { lib: _UniFFILib -> uniffiCheckContractApiVersion(lib) uniffiCheckApiChecksums(lib) FfiConverterTypeEventListener.register(lib) } } } fun uniffi_pubkymobile_fn_free_eventnotifier(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, ): Unit fun uniffi_pubkymobile_fn_init_callback_eventlistener(`callbackStub`: ForeignCallback,_uniffi_out_err: RustCallStatus, ): Unit fun uniffi_pubkymobile_fn_func_auth(`url`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_create_recovery_file(`secretKey`: RustBuffer.ByValue,`passphrase`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_decrypt_recovery_file(`recoveryFile`: RustBuffer.ByValue,`passphrase`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_delete_file(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_generate_secret_key(_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_get(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_list(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_parse_auth_url(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_publish(`recordName`: RustBuffer.ByValue,`recordContent`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_publish_https(`recordName`: RustBuffer.ByValue,`target`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_put(`url`: RustBuffer.ByValue,`content`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_remove_event_listener(_uniffi_out_err: RustCallStatus, ): Unit fun uniffi_pubkymobile_fn_func_resolve(`publicKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_resolve_https(`publicKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_session(`pubky`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_set_event_listener(`listener`: Long,_uniffi_out_err: RustCallStatus, ): Unit fun uniffi_pubkymobile_fn_func_sign_in(`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_sign_out(`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun uniffi_pubkymobile_fn_func_sign_up(`secretKey`: RustBuffer.ByValue,`homeserver`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun ffi_pubkymobile_rustbuffer_alloc(`size`: Int,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun ffi_pubkymobile_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun ffi_pubkymobile_rustbuffer_free(`buf`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, ): Unit fun ffi_pubkymobile_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Int,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun ffi_pubkymobile_rust_future_continuation_callback_set(`callback`: UniFffiRustFutureContinuationCallbackType, ): Unit fun ffi_pubkymobile_rust_future_poll_u8(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_u8(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_u8(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_u8(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Byte fun ffi_pubkymobile_rust_future_poll_i8(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_i8(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_i8(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_i8(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Byte fun ffi_pubkymobile_rust_future_poll_u16(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_u16(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_u16(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_u16(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Short fun ffi_pubkymobile_rust_future_poll_i16(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_i16(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_i16(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_i16(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Short fun ffi_pubkymobile_rust_future_poll_u32(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_u32(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_u32(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_u32(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Int fun ffi_pubkymobile_rust_future_poll_i32(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_i32(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_i32(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_i32(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Int fun ffi_pubkymobile_rust_future_poll_u64(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_u64(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_u64(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_u64(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Long fun ffi_pubkymobile_rust_future_poll_i64(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_i64(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_i64(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_i64(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Long fun ffi_pubkymobile_rust_future_poll_f32(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_f32(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_f32(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_f32(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Float fun ffi_pubkymobile_rust_future_poll_f64(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_f64(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_f64(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_f64(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Double fun ffi_pubkymobile_rust_future_poll_pointer(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_pointer(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_pointer(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_pointer(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Pointer fun ffi_pubkymobile_rust_future_poll_rust_buffer(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_rust_buffer(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_rust_buffer(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_rust_buffer(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): RustBuffer.ByValue fun ffi_pubkymobile_rust_future_poll_void(`handle`: Pointer,`uniffiCallback`: USize, ): Unit fun ffi_pubkymobile_rust_future_cancel_void(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_free_void(`handle`: Pointer, ): Unit fun ffi_pubkymobile_rust_future_complete_void(`handle`: Pointer,_uniffi_out_err: RustCallStatus, ): Unit fun uniffi_pubkymobile_checksum_func_auth( ): Short fun uniffi_pubkymobile_checksum_func_create_recovery_file( ): Short fun uniffi_pubkymobile_checksum_func_decrypt_recovery_file( ): Short fun uniffi_pubkymobile_checksum_func_delete_file( ): Short fun uniffi_pubkymobile_checksum_func_generate_secret_key( ): Short fun uniffi_pubkymobile_checksum_func_get( ): Short fun uniffi_pubkymobile_checksum_func_get_public_key_from_secret_key( ): Short fun uniffi_pubkymobile_checksum_func_list( ): Short fun uniffi_pubkymobile_checksum_func_parse_auth_url( ): Short fun uniffi_pubkymobile_checksum_func_publish( ): Short fun uniffi_pubkymobile_checksum_func_publish_https( ): Short fun uniffi_pubkymobile_checksum_func_put( ): Short fun uniffi_pubkymobile_checksum_func_remove_event_listener( ): Short fun uniffi_pubkymobile_checksum_func_resolve( ): Short fun uniffi_pubkymobile_checksum_func_resolve_https( ): Short fun uniffi_pubkymobile_checksum_func_session( ): Short fun uniffi_pubkymobile_checksum_func_set_event_listener( ): Short fun uniffi_pubkymobile_checksum_func_sign_in( ): Short fun uniffi_pubkymobile_checksum_func_sign_out( ): Short fun uniffi_pubkymobile_checksum_func_sign_up( ): Short fun uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred( ): Short fun ffi_pubkymobile_uniffi_contract_version( ): Int } private fun uniffiCheckContractApiVersion(lib: _UniFFILib) { // Get the bindings contract version from our ComponentInterface val bindings_contract_version = 24 // Get the scaffolding contract version by calling the into the dylib val scaffolding_contract_version = lib.ffi_pubkymobile_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_pubkymobile_checksum_func_auth() != 61378.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_create_recovery_file() != 55903.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_decrypt_recovery_file() != 59688.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_delete_file() != 57905.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_generate_secret_key() != 63116.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_get() != 21596.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_get_public_key_from_secret_key() != 23603.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_list() != 8522.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_parse_auth_url() != 29088.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_publish() != 20156.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_publish_https() != 14705.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_put() != 51107.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_remove_event_listener() != 6794.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_resolve() != 18303.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_resolve_https() != 34593.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_session() != 65177.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_set_event_listener() != 19468.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_sign_in() != 21006.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_sign_out() != 59116.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_func_sign_up() != 58756.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } if (lib.uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred() != 39865.toShort()) { throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") } } // Async support // Public interface members begin here. 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) 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()) 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): Int { val sizeForLength = 4 val sizeForString = value.length * 3 return sizeForLength + sizeForString } override fun write(value: String, buf: ByteBuffer) { val byteBuf = toUtf8(value) buf.putInt(byteBuf.limit()) buf.put(byteBuf) } } // 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) } } } 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 } } // The base class for all UniFFI Object types. // // This class provides core operations for working with the Rust `Arc` pointer to // 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 // the Kotlin wrapper object in lieu of reliable finalizers. The core requirements are: // // * Each `FFIObject` 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 `FFIObject` 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 `FFIObject` 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 will // leak 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. // // 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 `FFIObject` 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. // // In the future we may be able to replace some of this with automatic finalization logic, such as using // the new "Cleaner" functionaility in Java 9. The above scheme has been designed to work even if `destroy` is // invoked by garbage-collection machinery rather than by calling code (which by the way, it's apparently also // possible for the JVM to finalize an object while there is an in-flight call to one of its methods [1], // so there would still be some complexity here). // // Sigh...all of this for want of a robust finalization mechanism. // // [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219 // abstract class FFIObject( protected val pointer: Pointer ): Disposable, AutoCloseable { private val wasDestroyed = AtomicBoolean(false) private val callCounter = AtomicLong(1) open protected fun freeRustArcPtr() { // To be overridden in subclasses. } 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) { this.freeRustArcPtr() } } } @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.pointer) } finally { // This decrement always matches the increment we performed above. if (this.callCounter.decrementAndGet() == 0L) { this.freeRustArcPtr() } } } } public interface EventNotifierInterface { companion object } class EventNotifier( pointer: Pointer ) : FFIObject(pointer), EventNotifierInterface { /** * Disconnect the object from the underlying Rust object. * * It can be called more than once, but once called, interacting with the object * causes an `IllegalStateException`. * * Clients **must** call this method once done with the object, or cause a memory leak. */ override protected fun freeRustArcPtr() { rustCall() { status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_free_eventnotifier(this.pointer, status) } } companion object } public object FfiConverterTypeEventNotifier: FfiConverter { override fun lower(value: EventNotifier): Pointer = value.callWithPointer { it } override fun lift(value: Pointer): EventNotifier { return EventNotifier(value) } override fun read(buf: ByteBuffer): EventNotifier { // 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: EventNotifier) = 8 override fun write(value: EventNotifier, 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))) } } internal typealias Handle = Long internal class ConcurrentHandleMap( private val leftMap: MutableMap = mutableMapOf(), private val rightMap: MutableMap = mutableMapOf() ) { private val lock = java.util.concurrent.locks.ReentrantLock() private val currentHandle = AtomicLong(0L) private val stride = 1L fun insert(obj: T): Handle = lock.withLock { rightMap[obj] ?: currentHandle.getAndAdd(stride) .also { handle -> leftMap[handle] = obj rightMap[obj] = handle } } fun get(handle: Handle) = lock.withLock { leftMap[handle] } fun delete(handle: Handle) { this.remove(handle) } fun remove(handle: Handle): T? = lock.withLock { leftMap.remove(handle)?.let { obj -> rightMap.remove(obj) obj } } } interface ForeignCallback : com.sun.jna.Callback { public fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int } // 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 public abstract class FfiConverterCallbackInterface( protected val foreignCallback: ForeignCallback ): FfiConverter { private val handleMap = ConcurrentHandleMap() // Registers the foreign callback with the Rust side. // This method is generated for each callback interface. internal abstract fun register(lib: _UniFFILib) fun drop(handle: Handle): RustBuffer.ByValue { return handleMap.remove(handle).let { RustBuffer.ByValue() } } override fun lift(value: Handle): CallbackInterface { return handleMap.get(value) ?: throw InternalException("No callback in handlemap; this is a Uniffi bug") } override fun read(buf: ByteBuffer) = lift(buf.getLong()) override fun lower(value: CallbackInterface) = handleMap.insert(value).also { assert(handleMap.get(it) === value) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." } } override fun allocationSize(value: CallbackInterface) = 8 override fun write(value: CallbackInterface, buf: ByteBuffer) { buf.putLong(lower(value)) } } // Declaration and FfiConverters for EventListener Callback Interface public interface EventListener { fun `onEventOccurred`(`eventData`: String) companion object } // The ForeignCallback that is passed to Rust. internal class ForeignCallbackTypeEventListener : ForeignCallback { @Suppress("TooGenericExceptionCaught") override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { val cb = FfiConverterTypeEventListener.lift(handle) return when (method) { IDX_CALLBACK_FREE -> { FfiConverterTypeEventListener.drop(handle) // Successful return // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` UNIFFI_CALLBACK_SUCCESS } 1 -> { // Call the method, write to outBuf and return a status code // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for info try { this.`invokeOnEventOccurred`(cb, argsData, argsLen, outBuf) } catch (e: Throwable) { // Unexpected error try { // Try to serialize the error into a string outBuf.setValue(FfiConverterString.lower(e.toString())) } catch (e: Throwable) { // If that fails, then it's time to give up and just return } UNIFFI_CALLBACK_UNEXPECTED_ERROR } } else -> { // An unexpected error happened. // See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` try { // Try to serialize the error into a string outBuf.setValue(FfiConverterString.lower("Invalid Callback index")) } catch (e: Throwable) { // If that fails, then it's time to give up and just return } UNIFFI_CALLBACK_UNEXPECTED_ERROR } } } @Suppress("UNUSED_PARAMETER") private fun `invokeOnEventOccurred`(kotlinCallbackInterface: EventListener, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also { it.order(ByteOrder.BIG_ENDIAN) } fun makeCall() : Int { kotlinCallbackInterface.`onEventOccurred`( FfiConverterString.read(argsBuf) ) return UNIFFI_CALLBACK_SUCCESS } fun makeCallAndHandleError() : Int = makeCall() return makeCallAndHandleError() } } // The ffiConverter which transforms the Callbacks in to Handles to pass to Rust. public object FfiConverterTypeEventListener: FfiConverterCallbackInterface( foreignCallback = ForeignCallbackTypeEventListener() ) { override fun register(lib: _UniFFILib) { rustCall() { status -> lib.uniffi_pubkymobile_fn_init_callback_eventlistener(this.foreignCallback, status) } } } 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): Int { val sizeForLength = 4 val sizeForItems = value.map { FfiConverterString.allocationSize(it) }.sum() return sizeForLength + sizeForItems } override fun write(value: List, buf: ByteBuffer) { buf.putInt(value.size) value.forEach { FfiConverterString.write(it, buf) } } } fun `auth`(`url`: String, `secretKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_auth(FfiConverterString.lower(`url`),FfiConverterString.lower(`secretKey`),_status) }) } fun `createRecoveryFile`(`secretKey`: String, `passphrase`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_create_recovery_file(FfiConverterString.lower(`secretKey`),FfiConverterString.lower(`passphrase`),_status) }) } fun `decryptRecoveryFile`(`recoveryFile`: String, `passphrase`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_decrypt_recovery_file(FfiConverterString.lower(`recoveryFile`),FfiConverterString.lower(`passphrase`),_status) }) } fun `deleteFile`(`url`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_delete_file(FfiConverterString.lower(`url`),_status) }) } fun `generateSecretKey`(): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_generate_secret_key(_status) }) } fun `get`(`url`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_get(FfiConverterString.lower(`url`),_status) }) } fun `getPublicKeyFromSecretKey`(`secretKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(FfiConverterString.lower(`secretKey`),_status) }) } fun `list`(`url`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_list(FfiConverterString.lower(`url`),_status) }) } fun `parseAuthUrl`(`url`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_parse_auth_url(FfiConverterString.lower(`url`),_status) }) } fun `publish`(`recordName`: String, `recordContent`: String, `secretKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_publish(FfiConverterString.lower(`recordName`),FfiConverterString.lower(`recordContent`),FfiConverterString.lower(`secretKey`),_status) }) } fun `publishHttps`(`recordName`: String, `target`: String, `secretKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_publish_https(FfiConverterString.lower(`recordName`),FfiConverterString.lower(`target`),FfiConverterString.lower(`secretKey`),_status) }) } fun `put`(`url`: String, `content`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_put(FfiConverterString.lower(`url`),FfiConverterString.lower(`content`),_status) }) } fun `removeEventListener`() = rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_remove_event_listener(_status) } fun `resolve`(`publicKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_resolve(FfiConverterString.lower(`publicKey`),_status) }) } fun `resolveHttps`(`publicKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_resolve_https(FfiConverterString.lower(`publicKey`),_status) }) } fun `session`(`pubky`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_session(FfiConverterString.lower(`pubky`),_status) }) } fun `setEventListener`(`listener`: EventListener) = rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_set_event_listener(FfiConverterTypeEventListener.lower(`listener`),_status) } fun `signIn`(`secretKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_in(FfiConverterString.lower(`secretKey`),_status) }) } fun `signOut`(`secretKey`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_out(FfiConverterString.lower(`secretKey`),_status) }) } fun `signUp`(`secretKey`: String, `homeserver`: String): List { return FfiConverterSequenceString.lift( rustCall() { _status -> _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_up(FfiConverterString.lower(`secretKey`),FfiConverterString.lower(`homeserver`),_status) }) }