///|
/// A guest memory access outside the memory: the offset and the length.
#derive(Eq, Debug)
pub(all) suberror GuestMemoryError {
OutOfBounds(Int64, Int32)
}
///|
pub extend GuestMemoryError with Eq::{equal, not_equal}
///|
pub extend GuestMemoryError with @debug.Debug::{to_repr}
///|
/// The linear memory of a guest, as the sandbox sees it. The engine implements it for the memory of an
/// instance; a test can implement it over plain bytes. An access outside the memory raises
/// `OutOfBounds` and reads or writes nothing.
pub(open) trait GuestMemory {
/// The size of the memory in bytes.
fn size(Self) -> Int64
/// The `length` bytes at `offset`.
fn read(Self, Int64, Int32) -> Bytes raise GuestMemoryError
/// Writes the bytes at `offset`.
fn write(Self, Int64, BytesView) -> Unit raise GuestMemoryError
}
///|
fn little(value : UInt64, count : Int32) -> Bytes {
Bytes::makei(count, i => (value >> (8 * i)).to_byte())
}
///|
fn[M : GuestMemory] write_u32(
memory : M,
at : Int64,
value : Int64,
) -> Unit raise GuestMemoryError {
memory.write(at, little(value.reinterpret_as_uint64(), 4))
}
///|
fn[M : GuestMemory] write_u64(
memory : M,
at : Int64,
value : UInt64,
) -> Unit raise GuestMemoryError {
memory.write(at, little(value, 8))
}
///|
/// The most iovecs that one call takes.
pub let max_iovecs : Int32 = 1024
///|
/// Whether `length` bytes at `offset` are inside the memory.
fn[M : GuestMemory] inside(memory : M, offset : Int64, length : Int64) -> Bool {
offset >= 0L && length >= 0L && offset + length <= memory.size()
}
///|
/// The `(buffer, length)` pairs of an iovec array of at most `max_iovecs` entries, each of them inside
/// the memory. Gives `None` for too many iovecs. A pair outside the memory raises `OutOfBounds`, before
/// any byte of a buffer is read.
fn[M : GuestMemory] iovecs(
memory : M,
at : Int64,
count : Int64,
) -> Array[(Int64, Int64)]? raise GuestMemoryError {
guard count <= max_iovecs.to_int64() else { return None }
let entries = memory.read(at, (8L * count).to_int())
let word = (i : Int32) => {
entries[i].to_int64() |
(entries[i + 1].to_int64() << 8) |
(entries[i + 2].to_int64() << 16) |
(entries[i + 3].to_int64() << 24)
}
let pairs = Array::makei(count.to_int(), i => (word(8 * i), word(8 * i + 4)))
for pair in pairs {
guard inside(memory, pair.0, pair.1) else {
raise OutOfBounds(pair.0, pair.1.to_int())
}
}
Some(pairs)
}