diff --git a/runtime/src/main/java/com/dylibso/chicory/runtime/ByteArrayMemory.java b/runtime/src/main/java/com/dylibso/chicory/runtime/ByteArrayMemory.java index 12de4dedd..dc0fdad99 100644 --- a/runtime/src/main/java/com/dylibso/chicory/runtime/ByteArrayMemory.java +++ b/runtime/src/main/java/com/dylibso/chicory/runtime/ByteArrayMemory.java @@ -386,22 +386,22 @@ public byte read(int addr) { } @Override - public byte[] readBytes(int addr, int len) { + public void readBytesInto(int addr, byte[] buf, int destOffset, int len) { checkBounds(addr, len, sizeInBytes(), WasmRuntimeException::new); - byte[] result = new byte[len]; - int destOffset = 0; + if (destOffset < 0 || len > buf.length - destOffset) { + throw new IndexOutOfBoundsException(); + } int remaining = len; int a = addr; while (remaining > 0) { int pageIdx = a >>> PAGE_SHIFT; int pageOffset = a & PAGE_MASK; int chunk = Math.min(remaining, PAGE_SIZE - pageOffset); - System.arraycopy(pages[pageIdx], pageOffset, result, destOffset, chunk); + System.arraycopy(pages[pageIdx], pageOffset, buf, destOffset, chunk); a += chunk; destOffset += chunk; remaining -= chunk; } - return result; } @Override diff --git a/runtime/src/main/java/com/dylibso/chicory/runtime/ByteBufferMemory.java b/runtime/src/main/java/com/dylibso/chicory/runtime/ByteBufferMemory.java index dbcef602c..afe109e79 100644 --- a/runtime/src/main/java/com/dylibso/chicory/runtime/ByteBufferMemory.java +++ b/runtime/src/main/java/com/dylibso/chicory/runtime/ByteBufferMemory.java @@ -360,10 +360,11 @@ public byte read(int addr) { } @Override - public byte[] readBytes(int addr, int len) { + public void readBytesInto(int addr, byte[] buf, int destOffset, int len) { checkBounds(addr, len, sizeInBytes(), WasmRuntimeException::new); - byte[] result = new byte[len]; - int destOffset = 0; + if (destOffset < 0 || len > buf.length - destOffset) { + throw new IndexOutOfBoundsException(); + } int remaining = len; int a = addr; while (remaining > 0) { @@ -371,12 +372,11 @@ public byte[] readBytes(int addr, int len) { int pageOffset = a & PAGE_MASK; int chunk = Math.min(remaining, PAGE_SIZE - pageOffset); pages[pageIdx].position(pageOffset); - pages[pageIdx].get(result, destOffset, chunk); + pages[pageIdx].get(buf, destOffset, chunk); a += chunk; destOffset += chunk; remaining -= chunk; } - return result; } @Override diff --git a/runtime/src/main/java/com/dylibso/chicory/runtime/Memory.java b/runtime/src/main/java/com/dylibso/chicory/runtime/Memory.java index 7cf691d54..528e8feef 100644 --- a/runtime/src/main/java/com/dylibso/chicory/runtime/Memory.java +++ b/runtime/src/main/java/com/dylibso/chicory/runtime/Memory.java @@ -4,6 +4,12 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +/** + * Direct access to the memory (or a memory, if there are multiple) of the WASM virtual machine. + *
+ * By design, WASM is little-endian and methods that read and write numeric data types reflect that. For example, + * {@link #writeI32(int, int)} writes out the 4 bytes of an integer, starting with the least significant, at the address. + */ public interface Memory { /** @@ -365,7 +371,42 @@ default void write(int addr, byte[] data) { byte read(int addr); - byte[] readBytes(int addr, int len); + /** + * Reads an arbitrary number of bytes returning a filled buffer. + * + * @param addr the start address + * @param len how many bytes to read + * @return the read bytes (length will be equal to {@code len}) + * @throws WasmRuntimeException if the address and length specify an out-of-bounds memory range + */ + default byte[] readBytes(int addr, int len) { + byte[] result = new byte[len]; + readBytesInto(addr, result, 0, len); + return result; + } + + /** + * Reads an arbitrary number of bytes, sending them to the destination buffer starting from the offset. + * + * @param addr the start address + * @param buf the output buffer + * @param destOffset the offset in the output buffer from which to start writing + * @param len how many bytes to read + * @throws IndexOutOfBoundsException if {@code offset + len > buf.length} + * @throws WasmRuntimeException if the address and length specify an out-of-bounds memory range + */ + void readBytesInto(int addr, byte[] buf, int destOffset, int len); + + /** + * Reads an arbitrary number of bytes, completely filling the destination buffer. + * + * @param addr the start address + * @param buf the output buffer, whose length will be the number of bytes read + * @throws WasmRuntimeException if the address and buffer length specify an out-of-bounds memory range + */ + default void readBytesInto(int addr, byte[] buf) { + readBytesInto(addr, buf, 0, buf.length); + } void writeI32(int addr, int data);