Skip to content

Commit

Permalink
rework the memory reader api to require a buffer
Browse files Browse the repository at this point in the history
  • Loading branch information
CharlieTap committed Sep 7, 2024
1 parent 8420e45 commit 70af207
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,25 @@ import io.github.charlietap.chasm.embedding.shapes.Memory
import io.github.charlietap.chasm.embedding.shapes.map

inline fun HostFunctionContext.byte(
pointer: Int,
memory: Memory,
): ChasmResult<Byte, ChasmError.ExecutionError> = readByte(this.store, memory, pointer)
memoryPointer: Int,
): ChasmResult<Byte, ChasmError.ExecutionError> = readByte(this.store, memory, memoryPointer)

inline fun HostFunctionContext.bytes(
pointer: Int,
numberOfBytes: Int,
memory: Memory,
): ChasmResult<ByteArray, ChasmError.ExecutionError> = readBytes(this.store, memory, pointer, numberOfBytes)
buffer: ByteArray,
memoryPointer: Int,
bytesToRead: Int,
bufferPointer: Int = 0,
): ChasmResult<ByteArray, ChasmError.ExecutionError> = readBytes(this.store, memory, buffer, memoryPointer, bytesToRead, bufferPointer)

inline fun HostFunctionContext.int(
pointer: Int,
memory: Memory,
): ChasmResult<Int, ChasmError.ExecutionError> = readBytes(this.store, memory, pointer, Int.SIZE_BYTES).map { bytes ->
buffer: ByteArray,
memoryPointer: Int,
bufferPointer: Int = 0,
): ChasmResult<Int, ChasmError.ExecutionError> = readBytes(this.store, memory, buffer, memoryPointer, Int.SIZE_BYTES, bufferPointer).map {
bytes ->
var result: Int = 0
for (i in 0 until Int.SIZE_BYTES) {
result = result or (bytes[i].toInt() shl Byte.SIZE_BITS * i)
Expand All @@ -33,9 +38,12 @@ inline fun HostFunctionContext.int(
}

inline fun HostFunctionContext.uint(
pointer: Int,
memory: Memory,
): ChasmResult<UInt, ChasmError.ExecutionError> = readBytes(this.store, memory, pointer, Int.SIZE_BYTES).map { bytes ->
buffer: ByteArray,
memoryPointer: Int,
bufferPointer: Int = 0,
): ChasmResult<UInt, ChasmError.ExecutionError> = readBytes(this.store, memory, buffer, memoryPointer, UInt.SIZE_BYTES, bufferPointer).map {
bytes ->
var result: UInt = 0u
for (i in 0 until Int.SIZE_BYTES) {
result = result or (bytes[i].toUInt() shl (Byte.SIZE_BITS * i))
Expand All @@ -44,9 +52,12 @@ inline fun HostFunctionContext.uint(
}

inline fun HostFunctionContext.long(
pointer: Int,
memory: Memory,
): ChasmResult<Long, ChasmError.ExecutionError> = readBytes(this.store, memory, pointer, Long.SIZE_BYTES).map { bytes ->
buffer: ByteArray,
memoryPointer: Int,
bufferPointer: Int = 0,
): ChasmResult<Long, ChasmError.ExecutionError> = readBytes(this.store, memory, buffer, memoryPointer, Long.SIZE_BYTES, bufferPointer).map {
bytes ->
var result: Long = 0
for (i in 0 until Long.SIZE_BYTES) {
result = result or (bytes[i].toLong() shl Byte.SIZE_BITS * i)
Expand All @@ -55,9 +66,19 @@ inline fun HostFunctionContext.long(
}

inline fun HostFunctionContext.ulong(
pointer: Int,
memory: Memory,
): ChasmResult<ULong, ChasmError.ExecutionError> = readBytes(this.store, memory, pointer, Long.SIZE_BYTES).map { bytes ->
buffer: ByteArray,
memoryPointer: Int,
bufferPointer: Int = 0,
): ChasmResult<ULong, ChasmError.ExecutionError> = readBytes(
this.store,
memory,
buffer,
memoryPointer,
ULong.SIZE_BYTES,
bufferPointer,
).map {
bytes ->
var result: ULong = 0uL
for (i in 0 until Long.SIZE_BYTES) {
result = result or (bytes[i].toULong() shl (Byte.SIZE_BITS * i))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,38 @@ import io.github.charlietap.chasm.embedding.shapes.ChasmResult.Success
import io.github.charlietap.chasm.embedding.shapes.Memory
import io.github.charlietap.chasm.embedding.shapes.Store
import io.github.charlietap.chasm.executor.memory.read.MemoryInstanceBytesReader
import io.github.charlietap.chasm.executor.memory.read.MemoryInstanceBytesReaderImpl
import io.github.charlietap.chasm.executor.runtime.error.ModuleTrapError
import io.github.charlietap.chasm.executor.runtime.ext.memory

fun readBytes(
store: Store,
memory: Memory,
pointer: Int,
numberOfBytes: Int,
buffer: ByteArray,
memoryPointer: Int,
bytesToRead: Int,
bufferPointer: Int = 0,
): ChasmResult<ByteArray, ChasmError.ExecutionError> =
readBytes(
store = store,
memory = memory,
pointer = pointer,
numberOfBytes = numberOfBytes,
bytesReader = ::MemoryInstanceBytesReaderImpl,
buffer = buffer,
memoryPointer = memoryPointer,
bytesToRead = bytesToRead,
bufferPointer = bufferPointer,
bytesReader = ::MemoryInstanceBytesReader,
)
.mapError(ChasmError::ExecutionError)
.fold(::Success, ::Error)

internal fun readBytes(
store: Store,
memory: Memory,
pointer: Int,
numberOfBytes: Int,
buffer: ByteArray,
memoryPointer: Int,
bytesToRead: Int,
bufferPointer: Int,
bytesReader: MemoryInstanceBytesReader,
): Result<ByteArray, ModuleTrapError> = binding {
val instance = store.store.memory(memory.reference.address).bind()
bytesReader(instance, pointer, numberOfBytes).bind()
bytesReader(instance, buffer, memoryPointer, bytesToRead, bufferPointer).bind()
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ class ReadBytesTest {
val store = publicStore(store(memories = mutableListOf(instance)))
val address = memoryAddress()
val memory = publicMemory(memoryExternalValue(address))
val pointer = 118
val numberOfBytes = 2
val memoryPointer = 118
val bytesToRead = 2
val buffer = byteArrayOf()
val bufferPointer = 117
val bytes: ByteArray = byteArrayOf(117, 118)

val bytesReader: MemoryInstanceBytesReader = { _instance, _pointer, _numberOfBytes ->
val bytesReader: MemoryInstanceBytesReader = { _instance, _buffer, _memoryPointer, _bytesToRead, _bufferPointer ->
assertEquals(instance, _instance)
assertEquals(pointer, _pointer)
assertEquals(numberOfBytes, _numberOfBytes)
assertEquals(buffer, _buffer)
assertEquals(memoryPointer, _memoryPointer)
assertEquals(bytesToRead, _bytesToRead)
assertEquals(bufferPointer, _bufferPointer)

Ok(bytes)
}
Expand All @@ -39,8 +43,10 @@ class ReadBytesTest {
val actual = readBytes(
store = store,
memory = memory,
pointer = pointer,
numberOfBytes = numberOfBytes,
buffer = buffer,
memoryPointer = memoryPointer,
bytesToRead = bytesToRead,
bufferPointer = bufferPointer,
bytesReader = bytesReader,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ internal inline fun <T> LinearMemoryInteractorImpl(
val lastByte = offset + size

return if (offset >= 0 && size >= 0 && lastByte > 0 && lastByte <= memory.size()) {
Ok(operation())
try {
Ok(operation())
} catch (_: IndexOutOfBoundsException) {
Err(InvocationError.MemoryOperationOutOfBounds)
}
} else {
Err(InvocationError.MemoryOperationOutOfBounds)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,39 @@
@file:Suppress("NOTHING_TO_INLINE")

package io.github.charlietap.chasm.executor.memory.read

import com.github.michaelbull.result.Result
import io.github.charlietap.chasm.executor.memory.ByteArrayLinearMemory
import io.github.charlietap.chasm.executor.memory.LinearMemoryInteractor
import io.github.charlietap.chasm.executor.memory.LinearMemoryInteractorImpl
import io.github.charlietap.chasm.executor.runtime.error.InvocationError
import io.github.charlietap.chasm.executor.runtime.instance.MemoryInstance

typealias MemoryInstanceBytesReader = (MemoryInstance, Int, Int) -> Result<ByteArray, InvocationError.MemoryOperationOutOfBounds>
typealias MemoryInstanceBytesReader = (MemoryInstance, ByteArray, Int, Int, Int) -> Result<ByteArray, InvocationError.MemoryOperationOutOfBounds>

fun MemoryInstanceBytesReader(
instance: MemoryInstance,
buffer: ByteArray,
memoryPointer: Int,
bytesToRead: Int,
bufferPointer: Int,
): Result<ByteArray, InvocationError.MemoryOperationOutOfBounds> =
MemoryInstanceBytesReader(
instance = instance,
buffer = buffer,
memoryPointer = memoryPointer,
bytesToRead = bytesToRead,
bufferPointer = bufferPointer,
linearMemoryInteractor = ::LinearMemoryInteractorImpl,
)

internal inline fun MemoryInstanceBytesReader(
instance: MemoryInstance,
buffer: ByteArray,
memoryPointer: Int,
bytesToRead: Int,
bufferPointer: Int,
linearMemoryInteractor: LinearMemoryInteractor<ByteArray>,
): Result<ByteArray, InvocationError.MemoryOperationOutOfBounds> = linearMemoryInteractor(instance.data, memoryPointer, bytesToRead) {
(instance.data as ByteArrayLinearMemory).memory.copyInto(buffer, bufferPointer, memoryPointer, memoryPointer + bytesToRead)
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package io.github.charlietap.chasm.executor.memory.read

import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import io.github.charlietap.chasm.executor.memory.ByteArrayLinearMemory
import io.github.charlietap.chasm.executor.memory.LinearMemoryInteractorImpl
import io.github.charlietap.chasm.executor.runtime.error.InvocationError
import io.github.charlietap.chasm.executor.runtime.memory.LinearMemory
import io.github.charlietap.chasm.fixture.instance.memoryInstance
import kotlin.test.Test
import kotlin.test.assertContentEquals
import kotlin.test.assertEquals

class MemoryInstanceByteReaderTest {

@Test
fun `can read bytes from linear memory using a buffer `() {

val memoryArray = byteArrayOf(0, 0, 117, 118, 0, 0)
val memory = ByteArrayLinearMemory(min = LinearMemory.Pages(1), max = LinearMemory.Pages(1), memory = memoryArray)
val instance = memoryInstance(data = memory)

val memoryPointer = 2
val buffer = byteArrayOf(0, 0, 0, 0)
val bufferPointer = 2

val actual = MemoryInstanceBytesReader(
instance = instance,
buffer = buffer,
memoryPointer = memoryPointer,
bytesToRead = 2,
bufferPointer = bufferPointer,
linearMemoryInteractor = ::LinearMemoryInteractorImpl,
)

assertEquals(Ok(buffer), actual)
assertContentEquals(byteArrayOf(0, 0, 117, 118), buffer)
}

@Test
fun `error if returned if read is out of the memory's bounds `() {

val memory = ByteArrayLinearMemory(min = LinearMemory.Pages(1))
val instance = memoryInstance(data = memory)

val memoryPointer = LinearMemory.PAGE_SIZE
val buffer = byteArrayOf(0)
val bufferPointer = 0

val actual = MemoryInstanceBytesReader(
instance = instance,
buffer = buffer,
memoryPointer = memoryPointer,
bytesToRead = 1,
bufferPointer = bufferPointer,
linearMemoryInteractor = ::LinearMemoryInteractorImpl,
)

assertEquals(Err(InvocationError.MemoryOperationOutOfBounds), actual)
}

@Test
fun `error if returned if read is out of the buffer's bounds `() {

val memory = ByteArrayLinearMemory(min = LinearMemory.Pages(1))
val instance = memoryInstance(data = memory)

val memoryPointer = 0
val buffer = byteArrayOf(0)
val bufferPointer = 1

val actual = MemoryInstanceBytesReader(
instance = instance,
buffer = buffer,
memoryPointer = memoryPointer,
bytesToRead = 1,
bufferPointer = bufferPointer,
linearMemoryInteractor = ::LinearMemoryInteractorImpl,
)

assertEquals(Err(InvocationError.MemoryOperationOutOfBounds), actual)
}
}

0 comments on commit 70af207

Please sign in to comment.