experiments/MemLayout: add one more prototype

This commit is contained in:
Svyatoslav Scherbina
2016-09-06 15:18:38 +03:00
parent 399a90950a
commit 8faf375c1b
+242
View File
@@ -0,0 +1,242 @@
package _2
import bridge
import kotlin.reflect.KProperty
import java.io.Closeable
import NativeFunction
operator fun NativeFunction.invoke(vararg args: Any?): Long {
val convertedArgs = LongArray(args.size)
val allocatedArgs = mutableListOf<Ptr<*>>()
args.forEachIndexed { i, arg ->
val convertedArg = when (arg) {
null -> 0L
is Long -> arg
is Ptr<*> -> arg.ptr
is String -> {
val cString = CString.fromString(arg)
allocatedArgs.add(cString)
cString.ptr
}
is Byte -> arg.toLong()
is Char -> arg.toLong()
is Short -> arg.toLong()
is Int -> arg.toLong()
else -> throw NotImplementedError()
}
convertedArgs[i] = convertedArg
}
try {
return bridge.call(this.ptr, *convertedArgs)
} finally {
allocatedArgs.forEach { bridge.free(it.ptr) }
}
}
open class StructLayout(val size: Int)
open class Ptr<T : StructLayout>(val ptr: Long)
fun <T : StructLayout> alloc(layout: T): HeapPtr<T> {
return HeapPtr<T>(bridge.malloc(layout.size))
}
class HeapPtr<T : StructLayout>(ptr: Long) : Ptr<T>(ptr), Closeable {
override fun close() {
free()
}
fun free() {
bridge.free(ptr)
}
}
class ByteField(val offset: Int) {
operator fun getValue(thisRef: Ptr<*>, property: KProperty<*>): Byte {
return bridge.getByte(thisRef.ptr + offset)
}
operator fun setValue(thisRef: Ptr<*>, property: KProperty<*>, value: Byte) {
bridge.putByte(thisRef.ptr + offset, value)
}
}
class ShortField(val offset: Int) {
operator fun getValue(thisRef: Ptr<*>, property: KProperty<*>): Short {
return bridge.getShort(thisRef.ptr + offset)
}
operator fun setValue(thisRef: Ptr<*>, property: KProperty<*>, value: Short) {
bridge.putShort(thisRef.ptr + offset, value)
}
}
class IntField(val offset: Int) {
operator fun getValue(thisRef: Ptr<*>, property: KProperty<*>): Int {
return bridge.getInt(thisRef.ptr + offset)
}
operator fun setValue(thisRef: Ptr<*>, property: KProperty<*>, value: Int) {
bridge.putInt(thisRef.ptr + offset, value)
}
}
class LongField(val offset: Int) {
operator fun getValue(thisRef: Ptr<*>, property: KProperty<*>): Long {
return bridge.getLong(thisRef.ptr + offset)
}
operator fun setValue(thisRef: Ptr<*>, property: KProperty<*>, value: Long) {
bridge.putLong(thisRef.ptr + offset, value)
}
}
class PtrField<T : StructLayout>(val offset: Int) {
operator fun getValue(thisRef: Ptr<*>, property: KProperty<*>): Ptr<T> {
return Ptr<T>(bridge.getLong(thisRef.ptr + offset))
}
operator fun setValue(thisRef: Ptr<*>, property: KProperty<*>, value: Ptr<T>) {
bridge.putLong(thisRef.ptr + offset, value.ptr)
}
}
class StructField<T : StructLayout>(val offset: Int) {
operator fun getValue(thisRef: Ptr<*>, property: KProperty<*>) = Ptr<T>(thisRef.ptr + offset)
}
fun byteAt(offset: Int) = ByteField(offset)
fun shortAt(offset: Int) = ShortField(offset)
fun intAt(offset: Int) = IntField(offset)
fun longAt(offset: Int) = LongField(offset)
fun <T : StructLayout> ptrAt(offset: Int) = PtrField<T>(offset)
fun <T: StructLayout> structAt(offset: Int) = StructField<T>(offset)
class StructArray<T : StructLayout>(val elemLayout: T, length: Int) : StructLayout(elemLayout.size * length)
class PtrToStructArray<T : StructLayout>(ptr: Long, val elemLayout: StructLayout) : Ptr<StructArray<T>>(ptr) {
operator fun get(index: Int) = Ptr<T>(ptr + elemLayout.size * index)
}
fun <T : StructLayout> allocArray(layout: StructArray<T>) = PtrToStructArray<T>(alloc(layout).ptr, layout.elemLayout)
fun <T : StructLayout> allocArray(elemLayout: T, length: Int) = allocArray(StructArray(elemLayout, length))
object IntBox : StructLayout(4)
var Ptr<IntBox>.value by intAt(0)
class PtrBox<T : StructLayout> : StructLayout(8)
var <T : StructLayout> Ptr<PtrBox<T>>.value by ptrAt<T>(0)
class CString(length: Int) : StructLayout(length + 1) {
companion object {
fun fromString(str: String): Ptr<CString> {
val bytes = str.toByteArray(); // FIXME: encoding
val result = alloc(CString(bytes.size))
bytes.forEachIndexed { i, byte ->
result[i] = byte
}
result[bytes.size] = 0.toByte()
return result
}
}
}
operator fun Ptr<CString>.get(index: Int) = bridge.getByte(ptr + index)
operator fun Ptr<CString>.set(index: Int, value: Byte) = bridge.putByte(ptr + index, value)
fun Ptr<CString>.toKotlinString(): String {
var length = 0
while (this[length] != 0.toByte()) {
++length
}
val bytes = ByteArray(length)
for (i in 0 .. length-1) {
bytes.set(i, this[i])
}
return String(bytes) // FIXME: encoding
}
object timespec : StructLayout(16);
val Ptr<timespec>.tv_sec by longAt(0)
val Ptr<timespec>.tv_usec by longAt(8)
object stat64 : StructLayout(144);
val Ptr<stat64>.st_dev by intAt(0)
val Ptr<stat64>.st_mode by shortAt(4)
val Ptr<stat64>.st_nlink by shortAt(6)
val Ptr<stat64>.st_ino by longAt(8)
val Ptr<stat64>.st_atimespec by structAt<timespec>(32)
val Ptr<stat64>.st_mtimespec by structAt<timespec>(48)
val Ptr<stat64>.st_ctimespec by structAt<timespec>(64)
val Ptr<stat64>.st_birthtimespec by structAt<timespec>(80)
val Ptr<stat64>.st_size by longAt(96)
val stat64Fun = NativeFunction.lookup("stat\$INODE64")!!
object dirent64 : StructLayout(1048);
val Ptr<dirent64>.d_ino by longAt(0)
val Ptr<dirent64>.d_seekoff by longAt(8)
val Ptr<dirent64>.d_reclen by shortAt(16)
val Ptr<dirent64>.d_namelen by shortAt(18)
val Ptr<dirent64>.d_type by byteAt(20)
val Ptr<dirent64>.d_name by structAt<CString>(21)
val opendir = NativeFunction.lookup("opendir")!!
val readdir_r64 = NativeFunction.lookup("readdir_r\$INODE64")!!
val closedir = NativeFunction.lookup("closedir")!!
fun main(args: Array<String>) {
val array = allocArray(IntBox, 5)
array[0].value = 5
array[1].value = 6
println(array[0].value)
println(array[1].value)
alloc(stat64).use { s ->
val res = stat64Fun("/etc/passwd", s)
println(res)
with(s) {
println("st_dev=$st_dev")
println("st_mode=$st_mode")
println("st_nlink=$st_nlink")
println("st_ino=$st_ino")
println("st_mtimespec.tv_sec=${st_mtimespec.tv_sec}")
println("st_size=$st_size")
}
}
val dirp = opendir("/tmp")
try {
alloc(dirent64).use { entry ->
alloc(PtrBox<dirent64>()).use { result ->
while (true) {
val errno = readdir_r64(dirp, entry, result)
if (result.value.ptr == 0L) {
break
}
println(entry.d_name.toKotlinString())
}
}
}
} finally {
closedir(dirp)
}
};