Jeff Lockhart
09/18/2022, 12:37 AMTo get the pointer,should be allocated in native memory, e.g..cstr
Butval cString = kotlinString.cstr.getPointer(nativeHeap)
getPointer()
accepts an AutofreeScope
parameter, which nativeHeap
doesn't fulfill. Seems like it should be accepting a NativePlacement
instead. (Maybe it used to?)
Is there a more concise/idiomatic way to copy a Kotlin string to the native heap than something like this?
val string = "my kotlin string"
val bytes = string.encodeToByteArray()
val cPtr = nativeHeap.allocArray<ByteVar>(bytes.size + 1)
bytes.usePinned {
memcpy(cPtr, it.addressOf(0), bytes.size.convert())
}
Jeff Lockhart
09/18/2022, 1:03 AMNativePlacement
to AutofreeScope
. @svyatoslav.scherbina please, could you shed some light on this?Jeff Lockhart
09/18/2022, 1:51 AMgetPointer()
code with nativeHeap
instead of the Autofreescope
parameter looks as if it should work:
val string = "my kotlin string"
val cstr = string.cstr
val cPtr = cstr.place(interpretCPointer(nativeHeap.alloc(cstr.size, cstr.align).rawPtr)!!)
What is the reason this parameter type was changed?napperley
09/18/2022, 3:34 AM// ...
val kotlinStr = "A String"
fun kotlinFunc() = memScoped {
val cStr = kotlinStr.cstr
val ptr = kotlinStr.cstr.ptr
// Do something with cstr and/or ptr here.
}
napperley
09/18/2022, 3:40 AMnapperley
09/18/2022, 3:43 AMJeff Lockhart
09/18/2022, 3:45 AMsvyatoslav.scherbina
09/19/2022, 8:46 AMIs there a more concise/idiomatic way to copy a Kotlin string to the native heap than something like this?Yes:
val arena = Arena()
string.cstr.getPointer(arena)
// Somewhen later, to reclaim the memory:
arena.clear()
You can also reuse an Arena
instance for more strings.
Regarding your original example (if for some reason you would like to proceed with this approach instead):
bytes.usePinned {
memcpy(cPtr, it.addressOf(0), bytes.size.convert())
}
This can be simplified to
memcpy(cPtr, bytes.refTo(0), bytes.size.convert())
You might also find strdup
useful, instead of memcpy
.
What is the reason this parameter type was changed?To make
getPointer
more safe (so it is harder to accidentally allow memory leak) and more flexible (e.g. to implement `refTo`: https://github.com/JetBrains/kotlin/blob/2ba7c7b8a99626a28c0058cd1af628b588cf06a8/[…]e/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/Pinning.kt).
In this particular case I’m needing to pass a c string to be retained and used by the C library code. So I need the memory to be allocated on the native heap, rather than a temporary memScope.Does this C library call
free
on the string later?Jeff Lockhart
09/19/2022, 7:29 PMDoes this C library callThe C library binds the ownership of the string in this case to another object from the SDK, for example an Array or Dictionary object. When the Array or Dictionary is released, it frees the underlying string(s) and other memory allocated to objects within the data structure. Users of the C library are expected to callon the string later?free
retain()
and release()
on the data structure object via the library's manual reference counting API.svyatoslav.scherbina
09/20/2022, 7:28 AMThe C library binds the ownership of the string in this case to another object from the SDK, for example an Array or Dictionary object. When the Array or Dictionary is released, it frees the underlying string(s) and other memory allocated to objects within the data structure.Ok. Then allocating C string in an
Arena
won’t work, because it can’t be properly disposed with free
. Even nativeHeap
doesn’t guarantee that it uses malloc
under the hood, so whatever was allocated with nativeHeap.alloc
should be freed with nativeHeap.free
, not the regular C free
.
So the safest option here probably is to use strdup
, or malloc
+ memcpy
.Jeff Lockhart
09/20/2022, 10:33 PMstrdup
is probably the simplest API for this string copy use case, as well as strlen
, as I also need the string size in the struct API.
After reading some of the issues around destructor/finalizer mechanisms in Kotlin/Native, I found your comment here suggesting Cleaner
. This seems to be the best solution for ensuring wrapped native resources are released in classes that aren't AutoCloseable
in my multiplatform API.
It'd be great to have `AutoCloseable` in common stdlib as well. I've had to redefine the JVM class and Kotlin use
extension for my multiplatform project. Looks like this will hopefully be possible in Kotlin 1.8.