From fcb992128b52386cae83bd406d624f68fded8b44 Mon Sep 17 00:00:00 2001 From: Naomi Plasterer Date: Thu, 18 Jan 2024 20:35:50 -0800 Subject: [PATCH] first pass at all the pieces needed for threading --- .../java/org/xmtp/android/library/Client.kt | 48 +- .../org/xmtp/android/library/Conversation.kt | 29 +- .../org/xmtp/android/library/Conversations.kt | 20 +- .../java/org/xmtp/android/library/Group.kt | 26 + .../android/library/libxmtp/InboxOwner.kt | 5 + .../xmtp/android/library/libxmtp/Message.kt | 16 + .../android/library/libxmtp/XMTPLogger.kt | 10 + library/src/main/java/xmtpv3.kt | 2414 +++++++++++++++++ 8 files changed, 2559 insertions(+), 9 deletions(-) create mode 100644 library/src/main/java/org/xmtp/android/library/Group.kt create mode 100644 library/src/main/java/org/xmtp/android/library/libxmtp/InboxOwner.kt create mode 100644 library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt create mode 100644 library/src/main/java/org/xmtp/android/library/libxmtp/XMTPLogger.kt create mode 100644 library/src/main/java/xmtpv3.kt diff --git a/library/src/main/java/org/xmtp/android/library/Client.kt b/library/src/main/java/org/xmtp/android/library/Client.kt index 260b8927a..3911b4a33 100644 --- a/library/src/main/java/org/xmtp/android/library/Client.kt +++ b/library/src/main/java/org/xmtp/android/library/Client.kt @@ -34,6 +34,11 @@ import org.xmtp.android.library.messages.walletAddress import org.xmtp.proto.message.api.v1.MessageApiOuterClass import org.xmtp.proto.message.api.v1.MessageApiOuterClass.BatchQueryResponse import org.xmtp.proto.message.api.v1.MessageApiOuterClass.QueryRequest +import uniffi.xmtp_dh.FfiInboxOwner +import uniffi.xmtp_dh.FfiXmtpClient +import uniffi.xmtp_dh.createClient +import uniffi.xmtp_dh.org.xmtp.android.library.libxmtp.InboxOwner +import uniffi.xmtp_dh.org.xmtp.android.library.libxmtp.XMTPLogger import java.nio.charset.StandardCharsets import java.text.SimpleDateFormat import java.time.Instant @@ -63,6 +68,8 @@ class Client() { lateinit var apiClient: ApiClient lateinit var contacts: Contacts lateinit var conversations: Conversations + lateinit var logger: XMTPLogger + var libXMTPClient: FfiXmtpClient? = null companion object { private const val TAG = "Client" @@ -137,12 +144,15 @@ class Client() { address: String, privateKeyBundleV1: PrivateKeyBundleV1, apiClient: ApiClient, + libXmtpClient: FfiXmtpClient? = null ) : this() { - this.address = address + this.address = libXMTPClient?.accountAddress() ?: address this.privateKeyBundleV1 = privateKeyBundleV1 this.apiClient = apiClient this.contacts = Contacts(client = this) - this.conversations = Conversations(client = this) + this.conversations = Conversations(client = this, libXMTPConversations = libXmtpClient?.conversations()) + this.logger = XMTPLogger() + this.libXMTPClient = libXmtpClient } fun buildFrom(bundle: PrivateKeyBundleV1, options: ClientOptions? = null): Client { @@ -153,18 +163,44 @@ class Client() { return Client(address = address, privateKeyBundleV1 = bundle, apiClient = apiClient) } - fun create(account: SigningKey, options: ClientOptions? = null): Client { + fun create( + account: SigningKey, + options: ClientOptions? = null, + inboxOwner: InboxOwner? = null, + ): Client { val clientOptions = options ?: ClientOptions() val apiClient = GRPCApiClient(environment = clientOptions.api.env, secure = clientOptions.api.isSecure) - return create(account = account, apiClient = apiClient, options = options) + return create( + account = account, + apiClient = apiClient, + options = options, + inboxOwner = inboxOwner + ) } - fun create(account: SigningKey, apiClient: ApiClient, options: ClientOptions? = null): Client { + fun create( + account: SigningKey, + apiClient: ApiClient, + options: ClientOptions? = null, + inboxOwner: InboxOwner? = null, + ): Client { return runBlocking { try { val privateKeyBundleV1 = loadOrCreateKeys(account, apiClient, options) - val client = Client(account.address, privateKeyBundleV1, apiClient) + val libXMTPClient: FfiXmtpClient? = if (inboxOwner != null) { + createClient( + logger = logger, + ffiInboxOwner = inboxOwner, + host = "https://dev.xmtp.network:5556", + isSecure = true, + db = null, + encryptionKey = null + ) + } else { + null + } + val client = Client(account.address, privateKeyBundleV1, apiClient, libXMTPClient) client.ensureUserContactPublished() client } catch (e: java.lang.Exception) { diff --git a/library/src/main/java/org/xmtp/android/library/Conversation.kt b/library/src/main/java/org/xmtp/android/library/Conversation.kt index 9a5382f6b..21bc24b20 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversation.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversation.kt @@ -24,7 +24,9 @@ sealed class Conversation { data class V1(val conversationV1: ConversationV1) : Conversation() data class V2(val conversationV2: ConversationV2) : Conversation() - enum class Version { V1, V2 } + data class Group(val group: org.xmtp.android.library.Group) : Conversation() + + enum class Version { V1, V2, GROUP } // This indicates whether this a v1 or v2 conversation. val version: Version @@ -32,6 +34,7 @@ sealed class Conversation { return when (this) { is V1 -> Version.V1 is V2 -> Version.V2 + is Group -> Version.GROUP } } @@ -41,6 +44,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.sentAt is V2 -> conversationV2.createdAt + is Group -> TODO() } } @@ -50,6 +54,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.peerAddress is V2 -> conversationV2.peerAddress + is Group -> TODO() } } @@ -60,6 +65,7 @@ sealed class Conversation { return when (this) { is V1 -> null is V2 -> conversationV2.context.conversationId + is Group -> null } } @@ -68,6 +74,7 @@ sealed class Conversation { return when (this) { is V1 -> null is V2 -> conversationV2.keyMaterial + is Group -> null } } @@ -75,6 +82,7 @@ sealed class Conversation { val client: Client = when (this) { is V1 -> conversationV1.client is V2 -> conversationV2.client + is Group -> group.client } return client.contacts.consentList.state(address = peerAddress) } @@ -99,6 +107,7 @@ sealed class Conversation { .setKeyMaterial(conversationV2.keyMaterial.toByteString()), ), ).build() + is Group -> TODO() } } @@ -106,6 +115,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.decode(envelope) is V2 -> conversationV2.decodeEnvelope(envelope) + is Group -> TODO() } } @@ -127,6 +137,8 @@ sealed class Conversation { is V2 -> { conversationV2.prepareMessage(content = content, options = options) } + + is Group -> TODO() } } @@ -142,6 +154,8 @@ sealed class Conversation { is V2 -> { conversationV2.prepareMessage(encodedContent = encodedContent, options = options) } + + is Group -> TODO() } } @@ -149,6 +163,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.send(prepared = prepared) is V2 -> conversationV2.send(prepared = prepared) + is Group -> TODO() } } @@ -156,6 +171,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.send(content = content, options = options) is V2 -> conversationV2.send(content = content, options = options) + is Group -> TODO() } } @@ -163,6 +179,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.send(text = text, sendOptions, sentAt) is V2 -> conversationV2.send(text = text, sendOptions, sentAt) + is Group -> TODO() } } @@ -170,6 +187,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.send(encodedContent = encodedContent, options = options) is V2 -> conversationV2.send(encodedContent = encodedContent, options = options) + is Group -> TODO() } } @@ -184,6 +202,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.topic.description is V2 -> conversationV2.topic + is Group -> TODO() } } @@ -221,6 +240,8 @@ sealed class Conversation { after = after, direction = direction, ) + + is Group -> group.messages() } } @@ -233,6 +254,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.decryptedMessages(limit, before, after, direction) is V2 -> conversationV2.decryptedMessages(limit, before, after, direction) + is Group -> TODO() } } @@ -242,6 +264,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.decrypt(envelope) is V2 -> conversationV2.decrypt(envelope) + is Group -> TODO() } } @@ -251,6 +274,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.client is V2 -> conversationV2.client + is Group -> group.client } } @@ -262,6 +286,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.streamMessages() is V2 -> conversationV2.streamMessages() + is Group -> TODO() } } @@ -269,6 +294,7 @@ sealed class Conversation { return when (this) { is V1 -> conversationV1.streamDecryptedMessages() is V2 -> conversationV2.streamDecryptedMessages() + is Group -> TODO() } } @@ -276,6 +302,7 @@ sealed class Conversation { return when (this) { is V1 -> return conversationV1.streamEphemeral() is V2 -> return conversationV2.streamEphemeral() + is Group -> TODO() } } } diff --git a/library/src/main/java/org/xmtp/android/library/Conversations.kt b/library/src/main/java/org/xmtp/android/library/Conversations.kt index 71edc7b37..bbbc3f753 100644 --- a/library/src/main/java/org/xmtp/android/library/Conversations.kt +++ b/library/src/main/java/org/xmtp/android/library/Conversations.kt @@ -32,11 +32,13 @@ import org.xmtp.proto.keystore.api.v1.Keystore.TopicMap.TopicData import org.xmtp.proto.message.contents.Contact import org.xmtp.proto.message.contents.Invitation import org.xmtp.android.library.messages.DecryptedMessage +import uniffi.xmtp_dh.FfiConversations import java.util.Date data class Conversations( var client: Client, var conversationsByTopic: MutableMap = mutableMapOf(), + val libXMTPConversations: FfiConversations? = null, ) { companion object { @@ -81,6 +83,17 @@ data class Conversations( ) } + suspend fun newGroup(accountAddress: String): Group { + val group = libXMTPConversations?.createGroup(accountAddress) + ?: throw XMTPException("Client does not support Groups") + return Group(client, group) + } + + suspend fun listGroups(): List { + return libXMTPConversations?.list()?.map { Group(client, it) } + ?: throw XMTPException("Client does not support Groups") + } + /** * This creates a new [Conversation] using a specified address * @param peerAddress The address of the client that you want to start a new conversation @@ -174,7 +187,7 @@ data class Conversations( * Get the list of conversations that current user has * @return The list of [Conversation] that the current [Client] has. */ - fun list(): List { + fun list(includeGroups: Boolean = false): List { val newConversations = mutableListOf() val mostRecent = conversationsByTopic.values.maxOfOrNull { it.createdAt } val pagination = Pagination(after = mostRecent) @@ -203,7 +216,10 @@ data class Conversations( it.peerAddress != client.address && Topic.isValidTopic(it.topic) }.map { Pair(it.topic, it) } - // TODO(perf): use DB to persist + sort + if (includeGroups) { + val groups = runBlocking { listGroups() } + // conversationsByTopic += groups.map { Pair(it.id(), Conversation.Group(it) } + } return conversationsByTopic.values.sortedByDescending { it.createdAt } } diff --git a/library/src/main/java/org/xmtp/android/library/Group.kt b/library/src/main/java/org/xmtp/android/library/Group.kt new file mode 100644 index 000000000..c4916a9aa --- /dev/null +++ b/library/src/main/java/org/xmtp/android/library/Group.kt @@ -0,0 +1,26 @@ +package org.xmtp.android.library + +import org.xmtp.android.library.libxmtp.Message +import uniffi.xmtp_dh.FfiGroup +import uniffi.xmtp_dh.FfiListMessagesOptions + +class Group(val client: Client, val libXMTPGroup: FfiGroup) { + val id: List + get() = libXMTPGroup.id() + + suspend fun send(text: String) { + libXMTPGroup.send(contentBytes = text.toByteArray(Charsets.UTF_8).toUByteArray().toList()) + } + + suspend fun messages(): List { + return libXMTPGroup.findMessages( + opts = FfiListMessagesOptions( + sentBeforeNs = null, + sentAfterNs = null, + limit = null + ) + ).map { + Message(client, it) + } + } +} \ No newline at end of file diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/InboxOwner.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/InboxOwner.kt new file mode 100644 index 000000000..ab04d5dfb --- /dev/null +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/InboxOwner.kt @@ -0,0 +1,5 @@ +package uniffi.xmtp_dh.org.xmtp.android.library.libxmtp + +import uniffi.xmtp_dh.FfiInboxOwner + +interface InboxOwner : FfiInboxOwner {} \ No newline at end of file diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt new file mode 100644 index 000000000..737e218b2 --- /dev/null +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/Message.kt @@ -0,0 +1,16 @@ +package org.xmtp.android.library.libxmtp + +import org.xmtp.android.library.Client +import uniffi.xmtp_dh.FfiMessage + +data class Message(val client: Client, val libXMTPMessage: FfiMessage) { + val id: ByteArray + get() = libXMTPMessage.id + + val senderAddress: String + get() = libXMTPMessage.addrFrom + + fun text(): String { + return libXMTPMessage.content.decodeToString() + } +} \ No newline at end of file diff --git a/library/src/main/java/org/xmtp/android/library/libxmtp/XMTPLogger.kt b/library/src/main/java/org/xmtp/android/library/libxmtp/XMTPLogger.kt new file mode 100644 index 000000000..06caddfcf --- /dev/null +++ b/library/src/main/java/org/xmtp/android/library/libxmtp/XMTPLogger.kt @@ -0,0 +1,10 @@ +package uniffi.xmtp_dh.org.xmtp.android.library.libxmtp + +import android.util.Log +import uniffi.xmtp_dh.FfiLogger + +class XMTPLogger : FfiLogger { + override fun log(level: UInt, levelLabel: String, message: String) { + Log.i("$level $levelLabel", message) + } +} diff --git a/library/src/main/java/xmtpv3.kt b/library/src/main/java/xmtpv3.kt new file mode 100644 index 000000000..8e0d29b56 --- /dev/null +++ b/library/src/main/java/xmtpv3.kt @@ -0,0 +1,2414 @@ +// 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.xmtp_dh + +// 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 +import kotlin.coroutines.Continuation +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.delay +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine + +// 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_xmtpv3_rustbuffer_alloc(size, status).also { + if(it.data == null) { + throw RuntimeException("RustBuffer.alloc() returned null data pointer (size=${size})") + } + } + } + + internal fun free(buf: RustBuffer.ByValue) = rustCall() { status -> + _UniFFILib.INSTANCE.ffi_xmtpv3_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) { + map.remove(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 "uniffi_xmtpv3" +} + +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 = "xmtpv3") + .also { lib: _UniFFILib -> + uniffiCheckContractApiVersion(lib) + uniffiCheckApiChecksums(lib) + FfiConverterForeignExecutor.register(lib) + FfiConverterTypeFfiInboxOwner.register(lib) + FfiConverterTypeFfiLogger.register(lib) + FfiConverterTypeFfiMessageCallback.register(lib) + } + } + } + + fun uniffi_xmtpv3_fn_free_fficonversations(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_fficonversations_create_group(`ptr`: Pointer,`accountAddress`: RustBuffer.ByValue,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackPointer,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_fficonversations_list(`ptr`: Pointer,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackRustBuffer,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_free_ffigroup(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffigroup_add_members(`ptr`: Pointer,`accountAddresses`: RustBuffer.ByValue,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackByte,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffigroup_find_messages(`ptr`: Pointer,`opts`: RustBuffer.ByValue,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackRustBuffer,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffigroup_id(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun uniffi_xmtpv3_fn_method_ffigroup_remove_members(`ptr`: Pointer,`accountAddresses`: RustBuffer.ByValue,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackByte,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffigroup_send(`ptr`: Pointer,`contentBytes`: RustBuffer.ByValue,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackByte,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffigroup_stream(`ptr`: Pointer,`messageCallback`: Long,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackPointer,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_free_ffimessagestreamcloser(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffimessagestreamcloser_close(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_free_ffixmtpclient(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_method_ffixmtpclient_account_address(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun uniffi_xmtpv3_fn_method_ffixmtpclient_conversations(`ptr`: Pointer,_uniffi_out_err: RustCallStatus, + ): Pointer + fun uniffi_xmtpv3_fn_init_callback_ffiinboxowner(`callbackStub`: ForeignCallback,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_init_callback_ffilogger(`callbackStub`: ForeignCallback,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_init_callback_ffimessagecallback(`callbackStub`: ForeignCallback,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_func_create_client(`logger`: Long,`ffiInboxOwner`: Long,`host`: RustBuffer.ByValue,`isSecure`: Byte,`db`: RustBuffer.ByValue,`encryptionKey`: RustBuffer.ByValue,`uniffiExecutor`: USize,`uniffiCallback`: UniFfiFutureCallbackPointer,`uniffiCallbackData`: USize,_uniffi_out_err: RustCallStatus, + ): Unit + fun uniffi_xmtpv3_fn_func_diffie_hellman_k256(`privateKeyBytes`: RustBuffer.ByValue,`publicKeyBytes`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun uniffi_xmtpv3_fn_func_recover_address(`signatureBytes`: RustBuffer.ByValue,`predigestMessage`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun uniffi_xmtpv3_fn_func_verify_k256_sha256(`signedBy`: RustBuffer.ByValue,`message`: RustBuffer.ByValue,`signature`: RustBuffer.ByValue,`recoveryId`: Byte,_uniffi_out_err: RustCallStatus, + ): Byte + fun ffi_xmtpv3_rustbuffer_alloc(`size`: Int,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun ffi_xmtpv3_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun ffi_xmtpv3_rustbuffer_free(`buf`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus, + ): Unit + fun ffi_xmtpv3_rustbuffer_reserve(`buf`: RustBuffer.ByValue,`additional`: Int,_uniffi_out_err: RustCallStatus, + ): RustBuffer.ByValue + fun uniffi_xmtpv3_checksum_func_create_client( + ): Short + fun uniffi_xmtpv3_checksum_func_diffie_hellman_k256( + ): Short + fun uniffi_xmtpv3_checksum_func_recover_address( + ): Short + fun uniffi_xmtpv3_checksum_func_verify_k256_sha256( + ): Short + fun uniffi_xmtpv3_checksum_method_fficonversations_create_group( + ): Short + fun uniffi_xmtpv3_checksum_method_fficonversations_list( + ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_add_members( + ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_find_messages( + ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_id( + ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_remove_members( + ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_send( + ): Short + fun uniffi_xmtpv3_checksum_method_ffigroup_stream( + ): Short + fun uniffi_xmtpv3_checksum_method_ffimessagestreamcloser_close( + ): Short + fun uniffi_xmtpv3_checksum_method_ffixmtpclient_account_address( + ): Short + fun uniffi_xmtpv3_checksum_method_ffixmtpclient_conversations( + ): Short + fun uniffi_xmtpv3_checksum_method_ffiinboxowner_get_address( + ): Short + fun uniffi_xmtpv3_checksum_method_ffiinboxowner_sign( + ): Short + fun uniffi_xmtpv3_checksum_method_ffilogger_log( + ): Short + fun uniffi_xmtpv3_checksum_method_ffimessagecallback_on_message( + ): Short + fun uniffi_foreign_executor_callback_set(`callback`: UniFfiForeignExecutorCallback, + ): Unit + fun ffi_xmtpv3_uniffi_contract_version( + ): Int + +} + +private fun uniffiCheckContractApiVersion(lib: _UniFFILib) { + // Get the bindings contract version from our ComponentInterface + val bindings_contract_version = 23 + // Get the scaffolding contract version by calling the into the dylib + val scaffolding_contract_version = lib.ffi_xmtpv3_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_xmtpv3_checksum_func_create_client() != 23882.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_func_diffie_hellman_k256() != 23225.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_func_recover_address() != 45923.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_func_verify_k256_sha256() != 31332.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_fficonversations_create_group() != 30537.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_fficonversations_list() != 49805.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_add_members() != 24978.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_find_messages() != 30460.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_id() != 35243.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_remove_members() != 1645.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_send() != 55957.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffigroup_stream() != 10513.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffimessagestreamcloser_close() != 46827.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_account_address() != 65151.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffixmtpclient_conversations() != 31628.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffiinboxowner_get_address() != 2205.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffiinboxowner_sign() != 47939.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffilogger_log() != 56011.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } + if (lib.uniffi_xmtpv3_checksum_method_ffimessagecallback_on_message() != 59170.toShort()) { + throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project") + } +} + +// Public interface members begin here. + + +public object FfiConverterUByte: FfiConverter { + override fun lift(value: Byte): UByte { + return value.toUByte() + } + + override fun read(buf: ByteBuffer): UByte { + return lift(buf.get()) + } + + override fun lower(value: UByte): Byte { + return value.toByte() + } + + override fun allocationSize(value: UByte) = 1 + + override fun write(value: UByte, buf: ByteBuffer) { + buf.put(value.toByte()) + } +} + +public object FfiConverterUInt: FfiConverter { + override fun lift(value: Int): UInt { + return value.toUInt() + } + + override fun read(buf: ByteBuffer): UInt { + return lift(buf.getInt()) + } + + override fun lower(value: UInt): Int { + return value.toInt() + } + + override fun allocationSize(value: UInt) = 4 + + override fun write(value: UInt, buf: ByteBuffer) { + buf.putInt(value.toInt()) + } +} + +public object FfiConverterLong: FfiConverter { + override fun lift(value: Long): Long { + return value + } + + override fun read(buf: ByteBuffer): Long { + return buf.getLong() + } + + override fun lower(value: Long): Long { + return value + } + + override fun allocationSize(value: Long) = 8 + + override fun write(value: Long, buf: ByteBuffer) { + buf.putLong(value) + } +} + +public object FfiConverterBoolean: FfiConverter { + override fun lift(value: Byte): Boolean { + return value.toInt() != 0 + } + + override fun read(buf: ByteBuffer): Boolean { + return lift(buf.get()) + } + + override fun lower(value: Boolean): Byte { + return if (value) 1.toByte() else 0.toByte() + } + + override fun allocationSize(value: Boolean) = 1 + + override fun write(value: Boolean, buf: ByteBuffer) { + buf.put(lower(value)) + } +} + +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) + } +} + +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): Int { + return 4 + value.size + } + override fun write(value: ByteArray, buf: ByteBuffer) { + buf.putInt(value.size) + buf.put(value) + } +} + + +// 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 FfiConversationsInterface { + @Throws(GenericException::class) + suspend fun `createGroup`(`accountAddress`: String): FfiGroup@Throws(GenericException::class) + suspend fun `list`(): List +} + +class FfiConversations( + pointer: Pointer +) : FFIObject(pointer), FfiConversationsInterface { + + /** + * 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_xmtpv3_fn_free_fficonversations(this.pointer, status) + } + } + + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `createGroup`(`accountAddress`: String) : FfiGroup { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerTypeFfiGroup_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_fficonversations_create_group( + thisPtr, + FfiConverterString.lower(`accountAddress`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `list`() : List { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerSequenceTypeFfiGroup_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_fficonversations_list( + thisPtr, + + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + + + +} + +public object FfiConverterTypeFfiConversations: FfiConverter { + override fun lower(value: FfiConversations): Pointer = value.callWithPointer { it } + + override fun lift(value: Pointer): FfiConversations { + return FfiConversations(value) + } + + override fun read(buf: ByteBuffer): FfiConversations { + // 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: FfiConversations) = 8 + + override fun write(value: FfiConversations, 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))) + } +} + + + + +public interface FfiGroupInterface { + @Throws(GenericException::class) + suspend fun `addMembers`(`accountAddresses`: List)@Throws(GenericException::class) + suspend fun `findMessages`(`opts`: FfiListMessagesOptions): List + fun `id`(): List@Throws(GenericException::class) + suspend fun `removeMembers`(`accountAddresses`: List)@Throws(GenericException::class) + suspend fun `send`(`contentBytes`: List)@Throws(GenericException::class) + suspend fun `stream`(`messageCallback`: FfiMessageCallback): FfiMessageStreamCloser +} + +class FfiGroup( + pointer: Pointer +) : FFIObject(pointer), FfiGroupInterface { + + /** + * 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_xmtpv3_fn_free_ffigroup(this.pointer, status) + } + } + + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `addMembers`(`accountAddresses`: List) { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerVoid_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_add_members( + thisPtr, + FfiConverterSequenceString.lower(`accountAddresses`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `findMessages`(`opts`: FfiListMessagesOptions) : List { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerSequenceTypeFfiMessage_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_find_messages( + thisPtr, + FfiConverterTypeFfiListMessagesOptions.lower(`opts`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + override fun `id`(): List = + callWithPointer { + rustCall() { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_id(it, + + _status) + } + }.let { + FfiConverterSequenceUByte.lift(it) + } + + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `removeMembers`(`accountAddresses`: List) { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerVoid_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_remove_members( + thisPtr, + FfiConverterSequenceString.lower(`accountAddresses`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `send`(`contentBytes`: List) { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerVoid_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_send( + thisPtr, + FfiConverterSequenceUByte.lower(`contentBytes`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + + @Throws(GenericException::class) + @Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") + override suspend fun `stream`(`messageCallback`: FfiMessageCallback) : FfiMessageStreamCloser { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerTypeFfiMessageStreamCloser_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + callWithPointer { thisPtr -> + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffigroup_stream( + thisPtr, + FfiConverterTypeFfiMessageCallback.lower(`messageCallback`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } + } + + + +} + +public object FfiConverterTypeFfiGroup: FfiConverter { + override fun lower(value: FfiGroup): Pointer = value.callWithPointer { it } + + override fun lift(value: Pointer): FfiGroup { + return FfiGroup(value) + } + + override fun read(buf: ByteBuffer): FfiGroup { + // 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: FfiGroup) = 8 + + override fun write(value: FfiGroup, 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))) + } +} + + + + +public interface FfiMessageStreamCloserInterface { + + fun `close`() +} + +class FfiMessageStreamCloser( + pointer: Pointer +) : FFIObject(pointer), FfiMessageStreamCloserInterface { + + /** + * 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_xmtpv3_fn_free_ffimessagestreamcloser(this.pointer, status) + } + } + + override fun `close`() = + callWithPointer { + rustCall() { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffimessagestreamcloser_close(it, + + _status) + } + } + + + + + +} + +public object FfiConverterTypeFfiMessageStreamCloser: FfiConverter { + override fun lower(value: FfiMessageStreamCloser): Pointer = value.callWithPointer { it } + + override fun lift(value: Pointer): FfiMessageStreamCloser { + return FfiMessageStreamCloser(value) + } + + override fun read(buf: ByteBuffer): FfiMessageStreamCloser { + // 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: FfiMessageStreamCloser) = 8 + + override fun write(value: FfiMessageStreamCloser, 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))) + } +} + + + + +public interface FfiXmtpClientInterface { + + fun `accountAddress`(): String + fun `conversations`(): FfiConversations +} + +class FfiXmtpClient( + pointer: Pointer +) : FFIObject(pointer), FfiXmtpClientInterface { + + /** + * 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_xmtpv3_fn_free_ffixmtpclient(this.pointer, status) + } + } + + override fun `accountAddress`(): String = + callWithPointer { + rustCall() { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffixmtpclient_account_address(it, + + _status) + } + }.let { + FfiConverterString.lift(it) + } + + override fun `conversations`(): FfiConversations = + callWithPointer { + rustCall() { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_method_ffixmtpclient_conversations(it, + + _status) + } + }.let { + FfiConverterTypeFfiConversations.lift(it) + } + + + + +} + +public object FfiConverterTypeFfiXmtpClient: FfiConverter { + override fun lower(value: FfiXmtpClient): Pointer = value.callWithPointer { it } + + override fun lift(value: Pointer): FfiXmtpClient { + return FfiXmtpClient(value) + } + + override fun read(buf: ByteBuffer): FfiXmtpClient { + // 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: FfiXmtpClient) = 8 + + override fun write(value: FfiXmtpClient, 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 const val UNIFFI_RUST_TASK_CALLBACK_SUCCESS = 0.toByte() +internal const val UNIFFI_RUST_TASK_CALLBACK_CANCELLED = 1.toByte() +internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS = 0.toByte() +internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED = 1.toByte() +internal const val UNIFFI_FOREIGN_EXECUTOR_CALLBACK_ERROR = 2.toByte() + +// Callback function to execute a Rust task. The Kotlin code schedules these in a coroutine then +// invokes them. +internal interface UniFfiRustTaskCallback : com.sun.jna.Callback { + fun callback(rustTaskData: Pointer?, statusCode: Byte) +} + +internal object UniFfiForeignExecutorCallback : com.sun.jna.Callback { + fun callback(handle: USize, delayMs: Int, rustTask: UniFfiRustTaskCallback?, rustTaskData: Pointer?) : Byte { + if (rustTask == null) { + FfiConverterForeignExecutor.drop(handle) + return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS + } else { + val coroutineScope = FfiConverterForeignExecutor.lift(handle) + if (coroutineScope.isActive) { + val job = coroutineScope.launch { + if (delayMs > 0) { + delay(delayMs.toLong()) + } + rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_SUCCESS) + } + job.invokeOnCompletion { cause -> + if (cause != null) { + rustTask.callback(rustTaskData, UNIFFI_RUST_TASK_CALLBACK_CANCELLED) + } + } + return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_SUCCESS + } else { + return UNIFFI_FOREIGN_EXECUTOR_CALLBACK_CANCELLED + } + } + } +} + +public object FfiConverterForeignExecutor: FfiConverter { + internal val handleMap = UniFfiHandleMap() + + internal fun drop(handle: USize) { + handleMap.remove(handle) + } + + internal fun register(lib: _UniFFILib) { + lib.uniffi_foreign_executor_callback_set(UniFfiForeignExecutorCallback) + } + + // Number of live handles, exposed so we can test the memory management + public fun handleCount() : Int { + return handleMap.size + } + + override fun allocationSize(value: CoroutineScope) = USize.size + + override fun lift(value: USize): CoroutineScope { + return handleMap.get(value) ?: throw RuntimeException("unknown handle in FfiConverterForeignExecutor.lift") + } + + override fun read(buf: ByteBuffer): CoroutineScope { + return lift(USize.readFromBuffer(buf)) + } + + override fun lower(value: CoroutineScope): USize { + return handleMap.insert(value) + } + + override fun write(value: CoroutineScope, buf: ByteBuffer) { + lower(value).writeToBuffer(buf) + } +} + + + + +data class FfiListMessagesOptions ( + var `sentBeforeNs`: Long?, + var `sentAfterNs`: Long?, + var `limit`: Long? +) { + +} + +public object FfiConverterTypeFfiListMessagesOptions: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): FfiListMessagesOptions { + return FfiListMessagesOptions( + FfiConverterOptionalLong.read(buf), + FfiConverterOptionalLong.read(buf), + FfiConverterOptionalLong.read(buf), + ) + } + + override fun allocationSize(value: FfiListMessagesOptions) = ( + FfiConverterOptionalLong.allocationSize(value.`sentBeforeNs`) + + FfiConverterOptionalLong.allocationSize(value.`sentAfterNs`) + + FfiConverterOptionalLong.allocationSize(value.`limit`) + ) + + override fun write(value: FfiListMessagesOptions, buf: ByteBuffer) { + FfiConverterOptionalLong.write(value.`sentBeforeNs`, buf) + FfiConverterOptionalLong.write(value.`sentAfterNs`, buf) + FfiConverterOptionalLong.write(value.`limit`, buf) + } +} + + + + +data class FfiMessage ( + var `id`: ByteArray, + var `sentAtNs`: Long, + var `convoId`: ByteArray, + var `addrFrom`: String, + var `content`: ByteArray +) { + +} + +public object FfiConverterTypeFfiMessage: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): FfiMessage { + return FfiMessage( + FfiConverterByteArray.read(buf), + FfiConverterLong.read(buf), + FfiConverterByteArray.read(buf), + FfiConverterString.read(buf), + FfiConverterByteArray.read(buf), + ) + } + + override fun allocationSize(value: FfiMessage) = ( + FfiConverterByteArray.allocationSize(value.`id`) + + FfiConverterLong.allocationSize(value.`sentAtNs`) + + FfiConverterByteArray.allocationSize(value.`convoId`) + + FfiConverterString.allocationSize(value.`addrFrom`) + + FfiConverterByteArray.allocationSize(value.`content`) + ) + + override fun write(value: FfiMessage, buf: ByteBuffer) { + FfiConverterByteArray.write(value.`id`, buf) + FfiConverterLong.write(value.`sentAtNs`, buf) + FfiConverterByteArray.write(value.`convoId`, buf) + FfiConverterString.write(value.`addrFrom`, buf) + FfiConverterByteArray.write(value.`content`, buf) + } +} + + + + + +sealed class GenericException: Exception() { + // Each variant is a nested class + + class Generic( + val `err`: String + ) : GenericException() { + override val message + get() = "err=${ `err` }" + } + + + companion object ErrorHandler : CallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): GenericException = FfiConverterTypeGenericError.lift(error_buf) + } + + +} + +public object FfiConverterTypeGenericError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): GenericException { + + + return when(buf.getInt()) { + 1 -> GenericException.Generic( + FfiConverterString.read(buf), + ) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + } + + override fun allocationSize(value: GenericException): Int { + return when(value) { + is GenericException.Generic -> ( + // Add the size for the Int that specifies the variant plus the size needed for all fields + 4 + + FfiConverterString.allocationSize(value.`err`) + ) + } + } + + override fun write(value: GenericException, buf: ByteBuffer) { + when(value) { + is GenericException.Generic -> { + buf.putInt(1) + FfiConverterString.write(value.`err`, buf) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + + +sealed class SigningException(message: String): Exception(message) { + // Each variant is a nested class + // Flat enums carries a string error message, so no special implementation is necessary. + class Generic(message: String) : SigningException(message) + + + companion object ErrorHandler : CallStatusErrorHandler { + override fun lift(error_buf: RustBuffer.ByValue): SigningException = FfiConverterTypeSigningError.lift(error_buf) + } +} + +public object FfiConverterTypeSigningError : FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): SigningException { + + return when(buf.getInt()) { + 1 -> SigningException.Generic(FfiConverterString.read(buf)) + else -> throw RuntimeException("invalid error enum value, something is very wrong!!") + } + + } + + override fun allocationSize(value: SigningException): Int { + return 4 + } + + override fun write(value: SigningException, buf: ByteBuffer) { + when(value) { + is SigningException.Generic -> { + buf.putInt(1) + Unit + } + }.let { /* this makes the `when` an expression, which ensures it is exhaustive */ } + } + +} + + + + +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 FfiInboxOwner Callback Interface + +public interface FfiInboxOwner { + fun `getAddress`(): String + fun `sign`(`text`: String): ByteArray + +} + +// The ForeignCallback that is passed to Rust. +internal class ForeignCallbackTypeFfiInboxOwner : ForeignCallback { + @Suppress("TooGenericExceptionCaught") + override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + val cb = FfiConverterTypeFfiInboxOwner.lift(handle) + return when (method) { + IDX_CALLBACK_FREE -> { + FfiConverterTypeFfiInboxOwner.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.`invokeGetAddress`(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 + } + } + 2 -> { + // 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.`invokeSign`(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 `invokeGetAddress`(kotlinCallbackInterface: FfiInboxOwner, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + fun makeCall() : Int { + val returnValue = kotlinCallbackInterface.`getAddress`( + ) + outBuf.setValue(FfiConverterString.lowerIntoRustBuffer(returnValue)) + return UNIFFI_CALLBACK_SUCCESS + } + fun makeCallAndHandleError() : Int = makeCall() + + return makeCallAndHandleError() + } + + @Suppress("UNUSED_PARAMETER") + private fun `invokeSign`(kotlinCallbackInterface: FfiInboxOwner, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also { + it.order(ByteOrder.BIG_ENDIAN) + } + fun makeCall() : Int { + val returnValue = kotlinCallbackInterface.`sign`( + FfiConverterString.read(argsBuf) + + ) + outBuf.setValue(FfiConverterByteArray.lowerIntoRustBuffer(returnValue)) + return UNIFFI_CALLBACK_SUCCESS + } + fun makeCallAndHandleError() : Int = try { + makeCall() + } catch (e: SigningException) { + // Expected error, serialize it into outBuf + outBuf.setValue(FfiConverterTypeSigningError.lowerIntoRustBuffer(e)) + UNIFFI_CALLBACK_ERROR + } + + return makeCallAndHandleError() + } + +} + +// The ffiConverter which transforms the Callbacks in to Handles to pass to Rust. +public object FfiConverterTypeFfiInboxOwner: FfiConverterCallbackInterface( + foreignCallback = ForeignCallbackTypeFfiInboxOwner() +) { + override fun register(lib: _UniFFILib) { + rustCall() { status -> + lib.uniffi_xmtpv3_fn_init_callback_ffiinboxowner(this.foreignCallback, status) + } + } +} + + + + + + +// Declaration and FfiConverters for FfiLogger Callback Interface + +public interface FfiLogger { + fun `log`(`level`: UInt, `levelLabel`: String, `message`: String) + +} + +// The ForeignCallback that is passed to Rust. +internal class ForeignCallbackTypeFfiLogger : ForeignCallback { + @Suppress("TooGenericExceptionCaught") + override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + val cb = FfiConverterTypeFfiLogger.lift(handle) + return when (method) { + IDX_CALLBACK_FREE -> { + FfiConverterTypeFfiLogger.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.`invokeLog`(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 `invokeLog`(kotlinCallbackInterface: FfiLogger, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also { + it.order(ByteOrder.BIG_ENDIAN) + } + fun makeCall() : Int { + kotlinCallbackInterface.`log`( + FfiConverterUInt.read(argsBuf), + FfiConverterString.read(argsBuf), + 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 FfiConverterTypeFfiLogger: FfiConverterCallbackInterface( + foreignCallback = ForeignCallbackTypeFfiLogger() +) { + override fun register(lib: _UniFFILib) { + rustCall() { status -> + lib.uniffi_xmtpv3_fn_init_callback_ffilogger(this.foreignCallback, status) + } + } +} + + + + + + +// Declaration and FfiConverters for FfiMessageCallback Callback Interface + +public interface FfiMessageCallback { + fun `onMessage`(`message`: FfiMessage) + +} + +// The ForeignCallback that is passed to Rust. +internal class ForeignCallbackTypeFfiMessageCallback : ForeignCallback { + @Suppress("TooGenericExceptionCaught") + override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + val cb = FfiConverterTypeFfiMessageCallback.lift(handle) + return when (method) { + IDX_CALLBACK_FREE -> { + FfiConverterTypeFfiMessageCallback.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.`invokeOnMessage`(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 `invokeOnMessage`(kotlinCallbackInterface: FfiMessageCallback, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int { + val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also { + it.order(ByteOrder.BIG_ENDIAN) + } + fun makeCall() : Int { + kotlinCallbackInterface.`onMessage`( + FfiConverterTypeFfiMessage.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 FfiConverterTypeFfiMessageCallback: FfiConverterCallbackInterface( + foreignCallback = ForeignCallbackTypeFfiMessageCallback() +) { + override fun register(lib: _UniFFILib) { + rustCall() { status -> + lib.uniffi_xmtpv3_fn_init_callback_ffimessagecallback(this.foreignCallback, status) + } + } +} + + + + +public object FfiConverterOptionalLong: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): Long? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterLong.read(buf) + } + + override fun allocationSize(value: Long?): Int { + if (value == null) { + return 1 + } else { + return 1 + FfiConverterLong.allocationSize(value) + } + } + + override fun write(value: Long?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterLong.write(value, buf) + } + } +} + + + + +public object FfiConverterOptionalString: FfiConverterRustBuffer { + override fun read(buf: ByteBuffer): String? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterString.read(buf) + } + + override fun allocationSize(value: String?): Int { + if (value == null) { + return 1 + } else { + return 1 + FfiConverterString.allocationSize(value) + } + } + + override fun write(value: String?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterString.write(value, buf) + } + } +} + + + + +public object FfiConverterOptionalSequenceUByte: FfiConverterRustBuffer?> { + override fun read(buf: ByteBuffer): List? { + if (buf.get().toInt() == 0) { + return null + } + return FfiConverterSequenceUByte.read(buf) + } + + override fun allocationSize(value: List?): Int { + if (value == null) { + return 1 + } else { + return 1 + FfiConverterSequenceUByte.allocationSize(value) + } + } + + override fun write(value: List?, buf: ByteBuffer) { + if (value == null) { + buf.put(0) + } else { + buf.put(1) + FfiConverterSequenceUByte.write(value, buf) + } + } +} + + + + +public object FfiConverterSequenceUByte: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterUByte.read(buf) + } + } + + override fun allocationSize(value: List): Int { + val sizeForLength = 4 + val sizeForItems = value.map { FfiConverterUByte.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.forEach { + FfiConverterUByte.write(it, buf) + } + } +} + + + + +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) + } + } +} + + + + +public object FfiConverterSequenceTypeFfiGroup: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeFfiGroup.read(buf) + } + } + + override fun allocationSize(value: List): Int { + val sizeForLength = 4 + val sizeForItems = value.map { FfiConverterTypeFfiGroup.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.forEach { + FfiConverterTypeFfiGroup.write(it, buf) + } + } +} + + + + +public object FfiConverterSequenceTypeFfiMessage: FfiConverterRustBuffer> { + override fun read(buf: ByteBuffer): List { + val len = buf.getInt() + return List(len) { + FfiConverterTypeFfiMessage.read(buf) + } + } + + override fun allocationSize(value: List): Int { + val sizeForLength = 4 + val sizeForItems = value.map { FfiConverterTypeFfiMessage.allocationSize(it) }.sum() + return sizeForLength + sizeForItems + } + + override fun write(value: List, buf: ByteBuffer) { + buf.putInt(value.size) + value.forEach { + FfiConverterTypeFfiMessage.write(it, buf) + } + } +} +// Async return type handlers + + + + + + + + + + +// Stores all active future callbacks to ensure they're not GC'ed while waiting for the Rust code to +// complete the callback +val uniffiActiveFutureCallbacks = mutableSetOf() + +// FFI type for callback handlers +internal interface UniFfiFutureCallbackByte : com.sun.jna.Callback { + // Note: callbackData is always 0. We could pass Rust a pointer/usize to represent the + // continuation, but with JNA it's easier to just store it in the callback handler. + fun callback(_callbackData: USize, returnValue: Byte?, callStatus: RustCallStatus.ByValue); +} +internal interface UniFfiFutureCallbackPointer : com.sun.jna.Callback { + // Note: callbackData is always 0. We could pass Rust a pointer/usize to represent the + // continuation, but with JNA it's easier to just store it in the callback handler. + fun callback(_callbackData: USize, returnValue: Pointer?, callStatus: RustCallStatus.ByValue); +} +internal interface UniFfiFutureCallbackRustBuffer : com.sun.jna.Callback { + // Note: callbackData is always 0. We could pass Rust a pointer/usize to represent the + // continuation, but with JNA it's easier to just store it in the callback handler. + fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue); +} + +// Callback handlers for an async call. These are invoked by Rust when the future is ready. They +// lift the return value or error and resume the suspended function. + +internal class UniFfiFutureCallbackHandlerVoid(val continuation: Continuation) + : UniFfiFutureCallbackByte { + override fun callback(_callbackData: USize, returnValue: Byte?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(NullCallStatusErrorHandler, callStatus) + continuation.resume(Unit) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerVoid_TypeGenericError(val continuation: Continuation) + : UniFfiFutureCallbackByte { + override fun callback(_callbackData: USize, returnValue: Byte?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(Unit) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerBoolean_TypeGenericError(val continuation: Continuation) + : UniFfiFutureCallbackByte { + override fun callback(_callbackData: USize, returnValue: Byte?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterBoolean.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerString(val continuation: Continuation) + : UniFfiFutureCallbackRustBuffer { + override fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(NullCallStatusErrorHandler, callStatus) + continuation.resume(FfiConverterString.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerString_TypeGenericError(val continuation: Continuation) + : UniFfiFutureCallbackRustBuffer { + override fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterString.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerTypeFfiConversations(val continuation: Continuation) + : UniFfiFutureCallbackPointer { + override fun callback(_callbackData: USize, returnValue: Pointer?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(NullCallStatusErrorHandler, callStatus) + continuation.resume(FfiConverterTypeFfiConversations.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerTypeFfiGroup_TypeGenericError(val continuation: Continuation) + : UniFfiFutureCallbackPointer { + override fun callback(_callbackData: USize, returnValue: Pointer?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterTypeFfiGroup.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerTypeFfiMessageStreamCloser_TypeGenericError(val continuation: Continuation) + : UniFfiFutureCallbackPointer { + override fun callback(_callbackData: USize, returnValue: Pointer?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterTypeFfiMessageStreamCloser.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerTypeFfiXmtpClient_TypeGenericError(val continuation: Continuation) + : UniFfiFutureCallbackPointer { + override fun callback(_callbackData: USize, returnValue: Pointer?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterTypeFfiXmtpClient.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerSequenceUByte(val continuation: Continuation>) + : UniFfiFutureCallbackRustBuffer { + override fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(NullCallStatusErrorHandler, callStatus) + continuation.resume(FfiConverterSequenceUByte.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerSequenceUByte_TypeGenericError(val continuation: Continuation>) + : UniFfiFutureCallbackRustBuffer { + override fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterSequenceUByte.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerSequenceTypeFfiGroup_TypeGenericError(val continuation: Continuation>) + : UniFfiFutureCallbackRustBuffer { + override fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterSequenceTypeFfiGroup.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} + +internal class UniFfiFutureCallbackHandlerSequenceTypeFfiMessage_TypeGenericError(val continuation: Continuation>) + : UniFfiFutureCallbackRustBuffer { + override fun callback(_callbackData: USize, returnValue: RustBuffer.ByValue?, callStatus: RustCallStatus.ByValue) { + uniffiActiveFutureCallbacks.remove(this) + try { + checkCallStatus(GenericException, callStatus) + continuation.resume(FfiConverterSequenceTypeFfiMessage.lift(returnValue!!)) + } catch (e: Throwable) { + continuation.resumeWithException(e) + } + } +} +@Throws(GenericException::class) + +@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE") +suspend fun `createClient`(`logger`: FfiLogger, `ffiInboxOwner`: FfiInboxOwner, `host`: String, `isSecure`: Boolean, `db`: String?, `encryptionKey`: List?) : FfiXmtpClient { + // Create a new `CoroutineScope` for this operation, suspend the coroutine, and call the + // scaffolding function, passing it one of the callback handlers from `AsyncTypes.kt`. + return coroutineScope { + val scope = this + return@coroutineScope suspendCancellableCoroutine { continuation -> + try { + val callback = UniFfiFutureCallbackHandlerTypeFfiXmtpClient_TypeGenericError(continuation) + uniffiActiveFutureCallbacks.add(callback) + continuation.invokeOnCancellation { uniffiActiveFutureCallbacks.remove(callback) } + rustCall { status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_func_create_client( + FfiConverterTypeFfiLogger.lower(`logger`),FfiConverterTypeFfiInboxOwner.lower(`ffiInboxOwner`),FfiConverterString.lower(`host`),FfiConverterBoolean.lower(`isSecure`),FfiConverterOptionalString.lower(`db`),FfiConverterOptionalSequenceUByte.lower(`encryptionKey`), + FfiConverterForeignExecutor.lower(scope), + callback, + USize(0), + status, + ) + } + } catch (e: Exception) { + continuation.resumeWithException(e) + } + } + } +} +@Throws(GenericException::class) + +fun `diffieHellmanK256`(`privateKeyBytes`: List, `publicKeyBytes`: List): List { + return FfiConverterSequenceUByte.lift( + rustCallWithError(GenericException) { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_func_diffie_hellman_k256(FfiConverterSequenceUByte.lower(`privateKeyBytes`),FfiConverterSequenceUByte.lower(`publicKeyBytes`),_status) + }) +} + +@Throws(GenericException::class) + +fun `recoverAddress`(`signatureBytes`: List, `predigestMessage`: String): String { + return FfiConverterString.lift( + rustCallWithError(GenericException) { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_func_recover_address(FfiConverterSequenceUByte.lower(`signatureBytes`),FfiConverterString.lower(`predigestMessage`),_status) + }) +} + +@Throws(GenericException::class) + +fun `verifyK256Sha256`(`signedBy`: List, `message`: List, `signature`: List, `recoveryId`: UByte): Boolean { + return FfiConverterBoolean.lift( + rustCallWithError(GenericException) { _status -> + _UniFFILib.INSTANCE.uniffi_xmtpv3_fn_func_verify_k256_sha256(FfiConverterSequenceUByte.lower(`signedBy`),FfiConverterSequenceUByte.lower(`message`),FfiConverterSequenceUByte.lower(`signature`),FfiConverterUByte.lower(`recoveryId`),_status) + }) +} + +