// 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): ULong // Write a Kotlin type to a `ByteBuffer` fun write(value: KotlinType, buf: ByteBuffer) // Lower a value into a `RustBuffer` // // This method lowers a value into a `RustBuffer` rather than the normal // FfiType. It's used by the callback interface code. Callback interface // returns are always serialized into a `RustBuffer` regardless of their // normal FFI type. fun lowerIntoRustBuffer(value: KotlinType): RustBuffer.ByValue { val rbuf = RustBuffer.alloc(allocationSize(value)) try { val bbuf = rbuf.data!!.getByteBuffer(0, rbuf.capacity).also { it.order(ByteOrder.BIG_ENDIAN) } write(value, bbuf) rbuf.writeField("len", bbuf.position().toLong()) return rbuf } catch (e: Throwable) { RustBuffer.free(rbuf) throw e } } // Lift a value from a `RustBuffer`. // // This here mostly because of the symmetry with `lowerIntoRustBuffer()`. // It's currently only used by the `FfiConverterRustBuffer` class below. fun liftFromRustBuffer(rbuf: RustBuffer.ByValue): KotlinType { val byteBuf = rbuf.asByteBuffer()!! try { val item = read(byteBuf) if (byteBuf.hasRemaining()) { throw RuntimeException("junk remaining in buffer after lifting, something is very wrong!!") } return item } finally { RustBuffer.free(rbuf) } } } // FfiConverter that uses `RustBuffer` as the FfiType public interface FfiConverterRustBuffer: FfiConverter { override fun lift(value: RustBuffer.ByValue) = liftFromRustBuffer(value) override fun lower(value: KotlinType) = lowerIntoRustBuffer(value) }