Skip to content

Commit

Permalink
Merge pull request #8 from korlibs/soywiz/pointer.reading.and.callbac…
Browse files Browse the repository at this point in the history
…k.support

Initial work on pointer memory transfer and callbacks
  • Loading branch information
soywiz authored Jul 30, 2024
2 parents 075b557 + d5f3430 commit 0217732
Show file tree
Hide file tree
Showing 9 changed files with 366 additions and 39 deletions.
228 changes: 194 additions & 34 deletions korlibs-ffi-ksp/src@jvm/korlibs/ffi/ksp/FFIBuilderProcessor.kt

Large diffs are not rendered by default.

33 changes: 32 additions & 1 deletion korlibs-ffi/src/korlibs/ffi/api/FFI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,41 @@ inline class FFIPointer(val address: Long) {
}

class FFIFunctionRef<T : Function<*>>(val func: T) : AutoCloseable {
var address: Long? = null
var slot: Int = -1
var slots: Array<FFIFunctionRef<T>?>? = null
var closer: (() -> Unit)? = null

fun allocIn(slots: Array<FFIFunctionRef<T>?>): Int {
if (slot >= 0) return slot
slot = slots.indexOf(null)
this.slots = slots
if (slot == -1) error("No more slots available")
slots[slot] = this
return slot
}

override fun close() {
closer?.invoke()
closer = null
if (slot >= 0) this.slots?.set(slot, null)
slot = -1
this.slots = null
}
}
expect fun FFIPointer.getF64(offset: Int = 0): Double
expect fun FFIPointer.getF32(offset: Int = 0): Float
expect fun FFIPointer.getI64(offset: Int = 0): Long
expect fun FFIPointer.getI32(offset: Int = 0): Int
expect fun FFIPointer.getI16(offset: Int = 0): Short
expect fun FFIPointer.getI8(offset: Int = 0): Byte

expect fun FFIPointer.setF64(offset: Int = 0, value: Double)
expect fun FFIPointer.setF32(offset: Int = 0, value: Float)
expect fun FFIPointer.setI64(offset: Int = 0, value: Long)
expect fun FFIPointer.setI32(offset: Int = 0, value: Int)
expect fun FFIPointer.setI16(offset: Int = 0, value: Short)
expect fun FFIPointer.setI8(offset: Int = 0, value: Byte)

expect val isSupportedFFI: Boolean
internal expect fun transferMemory(address: Long, data: ByteArray, offset: Int, size: Int, toPointer: Boolean)
private val SCRATCH_MEM = ByteArray(64)
2 changes: 1 addition & 1 deletion korlibs-ffi/src/korlibs/ffi/api/TestMathFFI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ internal interface TestMathFFI : AutoCloseable {
fun cosf(v: Float): Float
fun malloc(size: Int): FFIPointer
fun free(ptr: FFIPointer)
//fun qsort(base: FFIPointer, number: Int, width: Int, compare: FFIFunctionRef<(FFIPointer, FFIPointer) -> Int>)
fun qsort(base: FFIPointer, number: Int, width: Int, compare: FFIFunctionRef<(FFIPointer, FFIPointer) -> Int>)
//fun puts(str: String): Int
//fun fputs(str: String, file: FFIPointer): Int
fun fopen(file: String, mode: String): FFIPointer
Expand Down
18 changes: 18 additions & 0 deletions korlibs-ffi/src@android/korlibs/ffi/api/FFI.android.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
package korlibs.ffi.api

actual val isSupportedFFI: Boolean = false

actual fun FFIPointer.getF64(offset: Int): Double = TODO()
actual fun FFIPointer.getF32(offset: Int): Float = TODO()
actual fun FFIPointer.getI64(offset: Int): Long = TODO()
actual fun FFIPointer.getI32(offset: Int): Int = TODO()
actual fun FFIPointer.getI16(offset: Int): Short = TODO()
actual fun FFIPointer.getI8(offset: Int): Byte = TODO()

actual fun FFIPointer.setF64(offset: Int, value: Double): Unit = TODO()
actual fun FFIPointer.setF32(offset: Int, value: Float): Unit = TODO()
actual fun FFIPointer.setI64(offset: Int, value: Long): Unit = TODO()
actual fun FFIPointer.setI32(offset: Int, value: Int): Unit = TODO()
actual fun FFIPointer.setI16(offset: Int, value: Short): Unit = TODO()
actual fun FFIPointer.setI8(offset: Int, value: Byte): Unit = TODO()

internal actual fun transferMemory(address: Long, data: ByteArray, offset: Int, size: Int, toPointer: Boolean) {
TODO()
}
31 changes: 31 additions & 0 deletions korlibs-ffi/src@js/korlibs/ffi/api/FFI.js.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
package korlibs.ffi.api

actual val isSupportedFFI: Boolean get() = js("(typeof Deno != 'undefined')")

private external val Deno: dynamic
private external class BigInt

private fun __toBigInt(str: String): BigInt = js("BigInt(str)")
private fun String.toBigInt(): BigInt = __toBigInt(this)
private fun Long.toBigInt(): BigInt = this.toString().toBigInt()
private fun BigInt.toLong(): Long = this.toString().toLong()
//private fun __createView(bigIntAddress: BigInt): dynamic = js("new Deno.UnsafePointerView(Deno.UnsafePointer.create(bigIntAddress))")
private fun __createView(bigIntAddress: BigInt, size: Int): dynamic = js("new DataView(Deno.UnsafePointerView.getArrayBuffer(Deno.UnsafePointer.create(bigIntAddress), size, 0))")
private fun createView(address: Long, size: Int) = __createView(address.toBigInt(), size) ?: error("Can't create view for address=$address")

actual fun FFIPointer.getF64(offset: Int): Double = createView(this.address + offset, 8).getFloat64(0, true)
actual fun FFIPointer.getF32(offset: Int): Float = createView(this.address + offset, 8).getFloat32(0, true)
actual fun FFIPointer.getI64(offset: Int): Long = createView(this.address + offset, 8).getBigInt64(0, true).unsafeCast<BigInt>().toLong()
actual fun FFIPointer.getI32(offset: Int): Int = createView(this.address + offset, 8).getInt32(0, true)
actual fun FFIPointer.getI16(offset: Int): Short = createView(this.address + offset, 8).getInt16(0, true)
actual fun FFIPointer.getI8(offset: Int): Byte = createView(this.address + offset, 8).getInt8(0)

actual fun FFIPointer.setF64(offset: Int, value: Double): Unit = createView(this.address + offset, 8).setFloat64(0, value, true)
actual fun FFIPointer.setF32(offset: Int, value: Float): Unit = createView(this.address + offset, 8).setFloat32(0, value, true)
actual fun FFIPointer.setI64(offset: Int, value: Long): Unit = createView(this.address + offset, 8).setBigInt64(0, value.toBigInt(), true)
actual fun FFIPointer.setI32(offset: Int, value: Int): Unit = createView(this.address + offset, 8).setInt32(0, value, true)
actual fun FFIPointer.setI16(offset: Int, value: Short): Unit = createView(this.address + offset, 8).setInt16(0, value, true)
actual fun FFIPointer.setI8(offset: Int, value: Byte): Unit = createView(this.address + offset, 8).setInt8(0, value)

internal actual fun transferMemory(address: Long, data: ByteArray, offset: Int, size: Int, toPointer: Boolean) {
//Deno.UnsafePointerView.copyInto()
//createView(address).pointer
TODO()
}
24 changes: 24 additions & 0 deletions korlibs-ffi/src@jvm/korlibs/ffi/api/FFI.jvm.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
package korlibs.ffi.api

import com.sun.jna.Pointer

actual val isSupportedFFI: Boolean = true

actual fun FFIPointer.getF64(offset: Int): Double = Pointer(address).getDouble(offset.toLong())
actual fun FFIPointer.getF32(offset: Int): Float = Pointer(address).getFloat(offset.toLong())
actual fun FFIPointer.getI64(offset: Int): Long = Pointer(address).getLong(offset.toLong())
actual fun FFIPointer.getI32(offset: Int): Int = Pointer(address).getInt(offset.toLong())
actual fun FFIPointer.getI16(offset: Int): Short = Pointer(address).getShort(offset.toLong())
actual fun FFIPointer.getI8(offset: Int): Byte = Pointer(address).getByte(offset.toLong())

actual fun FFIPointer.setF64(offset: Int, value: Double) = Pointer(address).setDouble(offset.toLong(), value)
actual fun FFIPointer.setF32(offset: Int, value: Float) = Pointer(address).setFloat(offset.toLong(), value)
actual fun FFIPointer.setI64(offset: Int, value: Long) = Pointer(address).setLong(offset.toLong(), value)
actual fun FFIPointer.setI32(offset: Int, value: Int) = Pointer(address).setInt(offset.toLong(), value)
actual fun FFIPointer.setI16(offset: Int, value: Short) = Pointer(address).setShort(offset.toLong(), value)
actual fun FFIPointer.setI8(offset: Int, value: Byte) = Pointer(address).setByte(offset.toLong(), value)

internal actual fun transferMemory(address: Long, data: ByteArray, offset: Int, size: Int, toPointer: Boolean) {
if (toPointer) {
Pointer(address).write(0, data, offset, size)
} else {
Pointer(address).read(0, data, offset, size)
}
}
30 changes: 30 additions & 0 deletions korlibs-ffi/src@native/korlibs/ffi/api/FFI.native.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,40 @@
package korlibs.ffi.api

import kotlinx.cinterop.*
import platform.posix.*
import kotlin.experimental.*

actual val isSupportedFFI: Boolean = true

actual fun FFIPointer.getF64(offset: Int): Double = (this.address + offset).toCPointer<DoubleVar>()!![0]
actual fun FFIPointer.getF32(offset: Int): Float = (this.address + offset).toCPointer<FloatVar>()!![0]
actual fun FFIPointer.getI64(offset: Int): Long = (this.address + offset).toCPointer<LongVar>()!![0]
actual fun FFIPointer.getI32(offset: Int): Int = (this.address + offset).toCPointer<IntVar>()!![0]
actual fun FFIPointer.getI16(offset: Int): Short = (this.address + offset).toCPointer<ShortVar>()!![0]
actual fun FFIPointer.getI8(offset: Int): Byte = (this.address + offset).toCPointer<ByteVar>()!![0]

actual fun FFIPointer.setF64(offset: Int, value: Double) { (this.address + offset).toCPointer<DoubleVar>()!![0] = value }
actual fun FFIPointer.setF32(offset: Int, value: Float) { (this.address + offset).toCPointer<FloatVar>()!![0] = value }
actual fun FFIPointer.setI64(offset: Int, value: Long) { (this.address + offset).toCPointer<LongVar>()!![0] = value }
actual fun FFIPointer.setI32(offset: Int, value: Int) { (this.address + offset).toCPointer<IntVar>()!![0] = value }
actual fun FFIPointer.setI16(offset: Int, value: Short) { (this.address + offset).toCPointer<ShortVar>()!![0] = value }
actual fun FFIPointer.setI8(offset: Int, value: Byte) { (this.address + offset).toCPointer<ByteVar>()!![0] = value }

@OptIn(ExperimentalForeignApi::class)
internal actual fun transferMemory(address: Long, data: ByteArray, offset: Int, size: Int, toPointer: Boolean) {
if (size == 0) return

data.usePinned {
val arrayPtr = it.addressOf(offset)
val pointerPtr = address.toCPointer<ByteVar>()
if (toPointer) {
memcpy(arrayPtr, pointerPtr, size.convert())
} else {
memcpy(pointerPtr, arrayPtr, size.convert())
}
}
}

expect fun FFIDLOpen(name: String): COpaquePointer?
expect fun FFIDLClose(lib: COpaquePointer?): Unit
expect fun FFIDLSym(lib: COpaquePointer?, name: String): COpaquePointer?
Expand Down
18 changes: 18 additions & 0 deletions korlibs-ffi/src@wasmJs/korlibs/ffi/api/FFI.wasmJs.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
package korlibs.ffi.api

actual val isSupportedFFI: Boolean = false

actual fun FFIPointer.getF64(offset: Int): Double = TODO()
actual fun FFIPointer.getF32(offset: Int): Float = TODO()
actual fun FFIPointer.getI64(offset: Int): Long = TODO()
actual fun FFIPointer.getI32(offset: Int): Int = TODO()
actual fun FFIPointer.getI16(offset: Int): Short = TODO()
actual fun FFIPointer.getI8(offset: Int): Byte = TODO()

actual fun FFIPointer.setF64(offset: Int, value: Double): Unit = TODO()
actual fun FFIPointer.setF32(offset: Int, value: Float): Unit = TODO()
actual fun FFIPointer.setI64(offset: Int, value: Long): Unit = TODO()
actual fun FFIPointer.setI32(offset: Int, value: Int): Unit = TODO()
actual fun FFIPointer.setI16(offset: Int, value: Short): Unit = TODO()
actual fun FFIPointer.setI8(offset: Int, value: Byte): Unit = TODO()

internal actual fun transferMemory(address: Long, data: ByteArray, offset: Int, size: Int, toPointer: Boolean) {
TODO()
}
21 changes: 18 additions & 3 deletions korlibs-ffi/test/korlibs/ffi/api/FFIGenerationTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,24 @@ class FFIGenerationTest {
assertEquals(1f, lib.cosf(0f))
//lib.puts("hello world\r\n")
val ptr = lib.malloc(40)
//lib.qsort(ptr, 10, 4, FFIFunctionRef { l, r -> 0 })
var old = 0
for (n in 0 until 10) {
ptr.setI32(n * 4, old)
old = old xor (n * 777777777)
}
assertEquals(
listOf(0, 0, 777777777, 1928074899, -100679232, 1083181060, -1487162319, -1317266793, -167789696, -2027935736),
(0 until 10).map { ptr.getI32(it * 4) }
)
FFIFunctionRef { l: FFIPointer, r: FFIPointer -> l.getI32().compareTo(r.getI32()) }.use {
lib.qsort(ptr, 10, 4, it)
}
assertEquals(
listOf(-2027935736, -1487162319, -1317266793, -167789696, -100679232, 0, 0, 777777777, 1083181060, 1928074899),
(0 until 10).map { ptr.getI32(it * 4) }
)
println(ptr)
lib.free(ptr)

lib.fopen("TEST.BIN", "wb").also { file ->
if (file != FFIPointer.NULL) {
Expand All @@ -23,8 +40,6 @@ class FFIGenerationTest {
}
lib.remove("TEST.BIN")

println(ptr)
lib.free(ptr)
println("AFTER FREE: $ptr")
}
}
Expand Down

0 comments on commit 0217732

Please sign in to comment.