Last active
January 2, 2024 20:17
-
-
Save curioustorvald/d60cf6b7ff7717a9694de741f22127b4 to your computer and use it in GitHub Desktop.
Java/Kotlin unsafe pointer for unsafe array
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.torvald | |
import sun.misc.Unsafe | |
/** | |
* Created by minjaesong on 2019-06-21. | |
*/ | |
object UnsafeHelper { | |
internal val unsafe: Unsafe | |
init { | |
val unsafeConstructor = Unsafe::class.java.getDeclaredConstructor() | |
unsafeConstructor.isAccessible = true | |
unsafe = unsafeConstructor.newInstance() | |
} | |
/** | |
* A factory method to allocate a memory of given size and return its starting address as a pointer. | |
*/ | |
fun allocate(size: Long): UnsafePtr { | |
val ptr = unsafe.allocateMemory(size) | |
return UnsafePtr(ptr, size) | |
} | |
fun memcpy(src: UnsafePtr, fromIndex: Long, dest: UnsafePtr, toIndex: Long, copyLength: Long) = | |
unsafe.copyMemory(src.ptr + fromIndex, dest.ptr + toIndex, copyLength) | |
fun memcpy(srcAddress: Long, destAddress: Long, copyLength: Long) = | |
unsafe.copyMemory(srcAddress, destAddress, copyLength) | |
fun memcpyRaw(srcObj: Any?, srcPos: Long, destObj: Any?, destPos: Long, len: Long) = | |
unsafe.copyMemory(srcObj, srcPos, destObj, destPos, len) | |
/** | |
* The array object in JVM is stored in this memory map: | |
* | |
* 0 w 2w * | |
* | Some identifier | Other identifier | the actual data ... | | |
* | |
* (where w = 4 for 32-bit JVM and 8 for 64-bit JVM. If Compressed-OOP is involved, things may get complicated) | |
* | |
* @return offset from the array's base memory address (aka pointer) that the actual data begins. | |
*/ | |
fun getArrayOffset(obj: Any) = unsafe.arrayBaseOffset(obj.javaClass) | |
} | |
/** | |
* To allocate a memory, use UnsafeHelper.allocate(long) | |
* | |
* All the getFloat/Int/whatever methods will follow the endianness of your system, | |
* e.g. it'll be Little Endian on x86, Big Endian on PPC, User-defined on ARM; therefore these functions should not be | |
* used when the portability matters (e.g. Savefile). In such situations, do byte-wise operations will be needed. | |
*/ | |
class UnsafePtr(val ptr: Long, val allocSize: Long) { | |
var destroyed = false | |
private set | |
fun destroy() { | |
if (!destroyed) { | |
UnsafeHelper.unsafe.freeMemory(ptr) | |
println("[UnsafePtr] Destroying pointer $this; called from:") | |
Thread.currentThread().stackTrace.forEach { println(it) } | |
destroyed = true | |
} | |
} | |
private inline fun checkNullPtr(index: Long) { // ignore what IDEA says and do inline this | |
if (destroyed) throw NullPointerException("The pointer is already destroyed ($this)") | |
// OOB Check: debugging purposes only -- comment out for the production | |
//if (index !in 0 until allocSize) throw IndexOutOfBoundsException("Index: $index; alloc size: $allocSize") | |
} | |
operator fun get(index: Long): Byte { | |
checkNullPtr(index) | |
return UnsafeHelper.unsafe.getByte(ptr + index) | |
} | |
fun getFloat(index: Long): Float { | |
checkNullPtr(index) | |
return UnsafeHelper.unsafe.getFloat(ptr + index) | |
} | |
fun getInt(index: Long): Int { | |
checkNullPtr(index) | |
return UnsafeHelper.unsafe.getInt(ptr + index) | |
} | |
operator fun set(index: Long, value: Byte) { | |
checkNullPtr(index) | |
UnsafeHelper.unsafe.putByte(ptr + index, value) | |
} | |
fun setFloat(index: Long, value: Float) { | |
checkNullPtr(index) | |
UnsafeHelper.unsafe.putFloat(ptr + index, value) | |
} | |
fun setInt(index: Long, value: Int) { | |
checkNullPtr(index) | |
UnsafeHelper.unsafe.putInt(ptr + index, value) | |
} | |
fun fillWith(byte: Byte) { | |
UnsafeHelper.unsafe.setMemory(ptr, allocSize, byte) | |
} | |
override fun toString() = "0x${ptr.toString(16)} with size $allocSize" | |
override fun equals(other: Any?) = this.ptr == (other as UnsafePtr).ptr | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment