-
Notifications
You must be signed in to change notification settings - Fork 5.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement KT-73565 and basic box tests for memcpy, memmove, memset and memcmp intrinsics #5385
base: master
Are you sure you want to change the base?
Changes from 2 commits
04ff86f
1c634a7
b2c8a21
07eb307
21dd4ab
ed67427
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ public interface NativeFreeablePlacement : NativePlacement { | |
|
||
@ExperimentalForeignApi | ||
public fun NativeFreeablePlacement.free(pointer: CPointer<*>): Unit = this.free(pointer.rawValue) | ||
|
||
@ExperimentalForeignApi | ||
public fun NativeFreeablePlacement.free(pointed: NativePointed): Unit = this.free(pointed.rawPtr) | ||
|
||
|
@@ -153,10 +154,10 @@ public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Int | |
*/ | ||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Long, | ||
initializer: T.(index: Long)->Unit): CArrayPointer<T> { | ||
initializer: T.(index: Long) -> Unit): CArrayPointer<T> { | ||
val res = allocArray<T>(length) | ||
|
||
(0 .. length - 1).forEach { index -> | ||
(0..length - 1).forEach { index -> | ||
res[index].initializer(index) | ||
} | ||
|
||
|
@@ -170,9 +171,9 @@ public inline fun <reified T : CVariable> NativePlacement.allocArray(length: Lon | |
*/ | ||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> NativePlacement.allocArray( | ||
length: Int, initializer: T.(index: Int)->Unit): CArrayPointer<T> = allocArray(length.toLong()) { index -> | ||
this.initializer(index.toInt()) | ||
} | ||
length: Int, initializer: T.(index: Int) -> Unit): CArrayPointer<T> = allocArray(length.toLong()) { index -> | ||
this.initializer(index.toInt()) | ||
} | ||
|
||
|
||
/** | ||
|
@@ -228,13 +229,9 @@ public fun NativePlacement.allocArrayOf(elements: ByteArray): CArrayPointer<Byte | |
|
||
@ExperimentalForeignApi | ||
public fun NativePlacement.allocArrayOf(vararg elements: Float): CArrayPointer<FloatVar> { | ||
val res = allocArray<FloatVar>(elements.size) | ||
var index = 0 | ||
while (index < elements.size) { | ||
res[index] = elements[index] | ||
++index | ||
return allocArray<FloatVar>(elements.size).apply { | ||
nativeMemUtils.putFloatArray(elements, pointed, elements.size) | ||
} | ||
return res | ||
} | ||
|
||
@ExperimentalForeignApi | ||
|
@@ -252,11 +249,13 @@ internal class ZeroValue<T : CVariable>(private val sizeBytes: Int, private val | |
nativeMemUtils.zeroMemory(interpretPointed(placement.rawValue), sizeBytes) | ||
return placement | ||
} | ||
|
||
override val size get() = sizeBytes | ||
|
||
override val align get() = alignBytes | ||
|
||
} | ||
|
||
@Suppress("NOTHING_TO_INLINE") | ||
@ExperimentalForeignApi | ||
public inline fun <T : CVariable> zeroValue(size: Int, align: Int): CValue<T> = ZeroValue(size, align) | ||
|
@@ -277,10 +276,12 @@ public fun <T : CVariable> CPointed.readValues(size: Int, align: Int): CValues<T | |
override fun getPointer(scope: AutofreeScope): CPointer<T> { | ||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!) | ||
} | ||
|
||
override fun place(placement: CPointer<T>): CPointer<T> { | ||
nativeMemUtils.putByteArray(bytes, interpretPointed(placement.rawValue), bytes.size) | ||
return placement | ||
} | ||
|
||
override val size get() = size | ||
override val align get() = align | ||
} | ||
|
@@ -300,10 +301,12 @@ public fun <T : CVariable> CPointed.readValue(size: Long, align: Int): CValue<T> | |
nativeMemUtils.putByteArray(bytes, interpretPointed(placement.rawValue), bytes.size) | ||
return placement | ||
} | ||
|
||
// Optimization to avoid unneeded virtual calls in base class implementation. | ||
public override fun getPointer(scope: AutofreeScope): CPointer<T> { | ||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!) | ||
} | ||
|
||
override val size get() = size.toInt() | ||
override val align get() = align | ||
} | ||
|
@@ -361,7 +364,7 @@ public inline fun <reified T : CStructVar> CValue<T>.copy(modify: T.() -> Unit): | |
|
||
@ExperimentalForeignApi | ||
public inline fun <reified T : CStructVar> cValue(initialize: T.() -> Unit): CValue<T> = | ||
zeroValue<T>().copy(modify = initialize) | ||
zeroValue<T>().copy(modify = initialize) | ||
|
||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> createValues(count: Int, initializer: T.(index: Int) -> Unit): CValues<T> = memScoped { | ||
|
@@ -379,6 +382,7 @@ public fun cValuesOf(vararg elements: Byte): CValues<ByteVar> = object : CValues | |
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> { | ||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!) | ||
} | ||
|
||
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> { | ||
nativeMemUtils.putByteArray(elements, interpretPointed(placement.rawValue), elements.size) | ||
return placement | ||
|
@@ -414,18 +418,25 @@ public fun <T : CPointed> cValuesOf(vararg elements: CPointer<T>?): CValues<CPoi | |
|
||
@ExperimentalForeignApi | ||
public fun ByteArray.toCValues(): CValues<ByteVar> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun ShortArray.toCValues(): CValues<ShortVar> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun IntArray.toCValues(): CValues<IntVar> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun LongArray.toCValues(): CValues<LongVar> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun FloatArray.toCValues(): CValues<FloatVar> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun DoubleArray.toCValues(): CValues<DoubleVar> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun <T : CPointed> Array<CPointer<T>?>.toCValues(): CValues<CPointerVar<T>> = cValuesOf(*this) | ||
|
||
@ExperimentalForeignApi | ||
public fun <T : CPointed> List<CPointer<T>?>.toCValues(): CValues<CPointerVar<T>> = this.toTypedArray().toCValues() | ||
|
||
|
@@ -438,6 +449,7 @@ private class CString(val bytes: ByteArray) : CValues<ByteVar>() { | |
override fun getPointer(scope: AutofreeScope): CPointer<ByteVar> { | ||
return place(interpretCPointer(scope.alloc(size, align).rawPtr)!!) | ||
} | ||
|
||
override fun place(placement: CPointer<ByteVar>): CPointer<ByteVar> { | ||
nativeMemUtils.putByteArray(bytes, placement.pointed, bytes.size) | ||
placement[bytes.size] = 0.toByte() | ||
|
@@ -497,7 +509,7 @@ public fun Array<String>.toCStringArray(autofreeScope: AutofreeScope): CPointer< | |
|
||
|
||
@ExperimentalForeignApi | ||
private class U16CString(val chars: CharArray): CValues<UShortVar>() { | ||
private class U16CString(val chars: CharArray) : CValues<UShortVar>() { | ||
override val size get() = 2 * (chars.size + 1) | ||
|
||
override val align get() = 2 | ||
|
@@ -593,13 +605,9 @@ public fun CPointer<ShortVar>.toKStringFromUtf16(): String { | |
while (nativeBytes[length] != 0.toShort()) { | ||
++length | ||
} | ||
val chars = CharArray(length) | ||
var index = 0 | ||
while (index < length) { | ||
chars[index] = nativeBytes[index].toInt().toChar() | ||
++index | ||
} | ||
return chars.concatToString() | ||
return CharArray(length).apply { | ||
nativeMemUtils.getCharArray(pointed, this, length) | ||
}.concatToString() | ||
} | ||
|
||
/** | ||
|
@@ -644,7 +652,7 @@ public fun CPointer<IntVar>.toKStringFromUtf32(): String { | |
*/ | ||
@SinceKotlin("1.3") | ||
@ExperimentalForeignApi | ||
public fun ByteArray.toKString() : String { | ||
public fun ByteArray.toKString(): String { | ||
val realEndIndex = realEndIndex(this, 0, this.size) | ||
return decodeToString(0, realEndIndex) | ||
} | ||
|
@@ -667,7 +675,7 @@ public fun ByteArray.toKString( | |
startIndex: Int = 0, | ||
endIndex: Int = this.size, | ||
throwOnInvalidSequence: Boolean = false | ||
) : String { | ||
): String { | ||
checkBoundsIndexes(startIndex, endIndex, this.size) | ||
val realEndIndex = realEndIndex(this, startIndex, endIndex) | ||
return decodeToString(startIndex, realEndIndex, throwOnInvalidSequence) | ||
|
@@ -696,7 +704,7 @@ public class MemScope : ArenaBase() { | |
public val memScope: MemScope | ||
get() = this | ||
|
||
public val <T: CVariable> CValues<T>.ptr: CPointer<T> | ||
public val <T : CVariable> CValues<T>.ptr: CPointer<T> | ||
get() = [email protected](this@MemScope) | ||
} | ||
|
||
|
@@ -708,7 +716,7 @@ public class MemScope : ArenaBase() { | |
*/ | ||
@ExperimentalForeignApi | ||
@OptIn(kotlin.contracts.ExperimentalContracts::class) | ||
public inline fun <R> memScoped(block: MemScope.()->R): R { | ||
public inline fun <R> memScoped(block: MemScope.() -> R): R { | ||
contract { | ||
callsInPlace(block, InvocationKind.EXACTLY_ONCE) | ||
} | ||
|
@@ -727,3 +735,43 @@ public fun COpaquePointer.readBytes(count: Int): ByteArray { | |
nativeMemUtils.getByteArray(this.reinterpret<ByteVar>().pointed, result, count) | ||
return result | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public fun setMemory(mem: NativePointed, value: Byte, size: Long) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you please add a small KDoc for all new public functions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do! |
||
nativeMemUtils.memset(mem, value, size) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public fun copyMemory(destMem: NativePointed, srcMem: NativePointed, size: Long) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need both moveMemory and copyMemory functions? The second one is less safe and performance benefits might not worth it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No real reason i added memmove apart from "might as well complete the set of three" when i was reading the LLVM manual to look up the copy intrinsic. Can remove if you want :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, I tend to think that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hm, not entirely happy with that to be honest, since 99% of the time you'd use memcpy, not memmove but that works. |
||
nativeMemUtils.memcpy(destMem, srcMem, size) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public fun moveMemory(destMem: NativePointed, srcMem: NativePointed, size: Long) { | ||
nativeMemUtils.memmove(destMem, srcMem, size) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public fun compareMemory(destMem: NativePointed, srcMem: NativePointed, size: Long): Int { | ||
return nativeMemUtils.memcmp(destMem, srcMem, size) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> CPointer<T>.setBlock(value: Byte, length: Int) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also a good name, will change. |
||
nativeMemUtils.memset(pointed, value, length * sizeOf<T>()) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> CPointer<T>.copyTo(dest: CPointer<T>, length: Int) { | ||
nativeMemUtils.memcpy(dest.pointed, pointed, length * sizeOf<T>()) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> CPointer<T>.moveTo(dest: CPointer<T>, length: Int) { | ||
nativeMemUtils.memmove(dest.pointed, pointed, length * sizeOf<T>()) | ||
} | ||
|
||
@ExperimentalForeignApi | ||
public inline fun <reified T : CVariable> CPointer<T>.compareBlock(dest: CPointer<T>, length: Int): Int { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that, will change. |
||
return nativeMemUtils.memcmp(dest.pointed, pointed, length * sizeOf<T>()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no need to add intrinsics to the JVM interop as they are unused there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, it didn't compile without them for me in my personal fork, that's odd. Will remove ^^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah i have to add them there too it seems, at least if i want to add this to nativeMemUtils, the code does not compile when the functions are not defined in both files. Everything breaks :/