I'm wondering if I am doing things correctly. Belo...
# kotlin-native
n
I'm wondering if I am doing things correctly. Below is what I have done so far (the IDE doesn't show any errors):
Copy code
public actual fun saveToBuffer(
        type: String,
        options: Map<String, String>,
        bufferSize: Int,
        error: Error?
    ): Pair<ByteArray, Boolean> = memScoped {
        val tmpBuffer = allocArray<CPointerVar<ByteVar>>(bufferSize)
        val arraySize = options.keys.size
        val keys = allocArray<CPointerVar<ByteVar>>(arraySize)
        val values = allocArray<CPointerVar<ByteVar>>(arraySize)
        options.keys.forEachIndexed { pos, item -> keys[pos]?.pointed?.value = item.toByte() }
        options.values.forEachIndexed { pos, item -> values[pos]?.pointed?.value = item.toByte() }
        val errorSet = gdk_pixbuf_save_to_bufferv(
            pixbuf = gdkPixbufPtr,
            type = type,
            error = cValuesOf(error?.gErrorPtr),
            option_keys = keys,
            option_values = values,
            buffer_size = null,
            buffer = tmpBuffer
        ) == TRUE
        val buffer = ByteArray(bufferSize)
        @Suppress("ReplaceRangeToWithUntil")
        (0..(bufferSize - 1)).forEach { pos ->
            val item = tmpBuffer[pos]
            if (item != null) buffer[pos] = item.pointed.value
        }
        buffer to errorSet
    }
1. Are manually allocated C arrays in Kotlin Native null terminated? 2. Is buffer a shallow copy of tmpBuffer? 3. Is there an easier way to do a shallow copy of a byte buffer that has been manually allocated to a new copy of ByteArray?
m
Copy code
val buffer = ByteArray(bufferSize)
buffer.usePinned { pinned ->
    ...
        buffer = pinned.addressOf(0)
    )
    ...
}
d
Manually allocated C arrays are calloc'd. So it's all zeros.
The way you create option keys and values is wrong. You can't use
toByte()
and your code with never execute it anyway because item is always null.
Ugh, I'm too lazy to type this in my phone. I'll finish this when I'm back from work.
m
They can have initializers, as normal Kotlin arrays:
Copy code
val keys = allocArray<CPointerVar<ByteVar>>(arraySize) { options.keys[it] }
1
d
Also, you're not supposed to allocate buffer yourself, the API gives you an allocated buffer. So you don't need the forEach at the end.
3. Yes there's a way to do the copy but I've forgotten what the extension function is called.
n
Below is the updated version:
Copy code
public actual fun saveToBuffer(
        type: String,
        options: Map<String, String>,
        bufferSize: Int,
        error: Error?
    ): Pair<ByteArray, Boolean> {
        val buffer = ByteArray(bufferSize)
        val (keys, values) = options.createByteArrays()
        var errorSet = false
        val keysPinned = keys.pin()
        val valuesPinned = values.pin()
        buffer.usePinned { bufferPinned ->
            errorSet = gdk_pixbuf_save_to_bufferv(
                pixbuf = gdkPixbufPtr,
                type = type,
                error = cValuesOf(error?.gErrorPtr),
                option_keys = cValuesOf(keysPinned.addressOf(0)),
                option_values = cValuesOf(valuesPinned.addressOf(0)),
                buffer_size = null,
                buffer = cValuesOf(bufferPinned.addressOf(0))
            ) == TRUE
        }
        keysPinned.unpin()
        valuesPinned.unpin()
        return buffer to errorSet
    }
Is only the first address of the array required for the option_keys, option_values, and buffer parameters?
This is becoming quite the mental workout, especially with cramming Kotlin objects (string collections) into CValuesRef<CPointerVar<ByteVar>> 💪 😵.