// Canonical linear-memory access uses 64-bit byte offsets on every engine.
///|
fn Memory::check_bounds_at(
self : Memory,
offset : Int64,
length : Int64,
) -> Unit raise RuntimeError {
self.ensure_memory_open()
if offset < 0L ||
length < 0L ||
length > self.byte_len() ||
offset > self.byte_len() - length {
raise OutOfBoundsMemoryAccess
}
}
///|
#cfg(target="native")
fn Memory::read_fixed_at(
self : Memory,
addr : Int64,
len : Int32,
) -> FixedArray[Byte] {
let pointer = self.desc_ptr()
if pointer.is_null() || len <= 0 {
return FixedArray::make(0, b'\x00')
}
let out = FixedArray::make(len, b'\x00')
@native_bridge.memory_descriptor_read(pointer, addr, out, len) |> ignore
out
}
///|
#cfg(target="native")
fn Memory::write_fixed_at(
self : Memory,
addr : Int64,
data : FixedArray[Byte],
len : Int32,
) -> Unit {
let pointer = self.desc_ptr()
if pointer.is_null() || len <= 0 {
return
}
@native_bridge.memory_descriptor_write(pointer, addr, data, len) |> ignore
}
///|
fn Memory::load_byte_unchecked_at(self : Memory, addr : Int64) -> Byte {
let bytes = self.read_fixed_at(addr, 1)
if bytes.length() == 1 {
bytes[0]
} else {
b'\x00'
}
}
///|
fn Memory::store_byte_unchecked_at(
self : Memory,
addr : Int64,
value : Byte,
) -> Unit {
let data = FixedArray::make(1, value)
self.write_fixed_at(addr, data, 1)
}
///|
pub fn Memory::load_byte_at(
self : Memory,
addr : Int64,
) -> Byte raise RuntimeError {
self.check_bounds_at(addr, 1)
self.load_byte_unchecked_at(addr)
}
///|
pub fn Memory::store_byte_at(
self : Memory,
addr : Int64,
value : Byte,
) -> Unit raise RuntimeError {
self.check_bounds_at(addr, 1)
self.store_byte_unchecked_at(addr, value)
}
///|
pub fn Memory::load_i32_at(
self : Memory,
addr : Int64,
) -> Int32 raise RuntimeError {
self.check_bounds_at(addr, 4)
let b = self.read_fixed_at(addr, 4)
let b0 = b[0].to_int()
let b1 = b[1].to_int()
let b2 = b[2].to_int()
let b3 = b[3].to_int()
b0 | (b1 << 8) | (b2 << 16) | (b3 << 24)
}
///|
pub fn Memory::store_i32_at(
self : Memory,
addr : Int64,
value : Int32,
) -> Unit raise RuntimeError {
self.check_bounds_at(addr, 4)
let data = FixedArray::make(4, b'\x00')
data[0] = value.land(0xFF).to_byte()
data[1] = (value >> 8).land(0xFF).to_byte()
data[2] = (value >> 16).land(0xFF).to_byte()
data[3] = (value >> 24).land(0xFF).to_byte()
self.write_fixed_at(addr, data, 4)
}
///|
pub fn Memory::load_i64_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
self.check_bounds_at(addr, 8)
let b = self.read_fixed_at(addr, 8)
let mut result = 0L
for i in 0..<8 {
result = result | (b[i].to_int64() << (i * 8))
}
result
}
///|
pub fn Memory::store_i64_at(
self : Memory,
addr : Int64,
value : Int64,
) -> Unit raise RuntimeError {
self.check_bounds_at(addr, 8)
let data = FixedArray::make(8, b'\x00')
for i in 0..<8 {
data[i] = (value >> (i * 8)).land(0xFFL).to_int().to_byte()
}
self.write_fixed_at(addr, data, 8)
}
///|
pub fn Memory::load_f32_at(
self : Memory,
addr : Int64,
) -> Float raise RuntimeError {
let bits = self.load_i32_at(addr)
Float::reinterpret_from_int(bits)
}
///|
pub fn Memory::store_f32_at(
self : Memory,
addr : Int64,
value : Float,
) -> Unit raise RuntimeError {
let bits = value.reinterpret_as_int()
self.store_i32_at(addr, bits)
}
///|
pub fn Memory::load_f64_at(
self : Memory,
addr : Int64,
) -> Double raise RuntimeError {
let bits = self.load_i64_at(addr)
bits.reinterpret_as_double()
}
///|
pub fn Memory::store_f64_at(
self : Memory,
addr : Int64,
value : Double,
) -> Unit raise RuntimeError {
let bits = value.reinterpret_as_int64()
self.store_i64_at(addr, bits)
}
///|
/// Load 8-bit signed integer, sign-extend to i32
pub fn Memory::load_i32_8s_at(
self : Memory,
addr : Int64,
) -> Int32 raise RuntimeError {
let byte = self.load_byte_at(addr).to_int()
if byte >= 128 {
byte - 256
} else {
byte
}
}
///|
/// Load 8-bit unsigned integer, zero-extend to i32
pub fn Memory::load_i32_8u_at(
self : Memory,
addr : Int64,
) -> Int32 raise RuntimeError {
self.load_byte_at(addr).to_int()
}
///|
/// Load 16-bit signed integer, sign-extend to i32
pub fn Memory::load_i32_16s_at(
self : Memory,
addr : Int64,
) -> Int32 raise RuntimeError {
self.check_bounds_at(addr, 2)
let b = self.read_fixed_at(addr, 2)
let b0 = b[0].to_int()
let b1 = b[1].to_int()
let value = b0 | (b1 << 8)
if value >= 32768 {
value - 65536
} else {
value
}
}
///|
/// Load 16-bit unsigned integer, zero-extend to i32
pub fn Memory::load_i32_16u_at(
self : Memory,
addr : Int64,
) -> Int32 raise RuntimeError {
self.check_bounds_at(addr, 2)
let b = self.read_fixed_at(addr, 2)
let b0 = b[0].to_int()
let b1 = b[1].to_int()
b0 | (b1 << 8)
}
///|
/// Load 8-bit signed integer, sign-extend to i64
pub fn Memory::load_i64_8s_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
let byte = self.load_byte_at(addr).to_int64()
if byte >= 128L {
byte - 256L
} else {
byte
}
}
///|
/// Load 8-bit unsigned integer, zero-extend to i64
pub fn Memory::load_i64_8u_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
self.load_byte_at(addr).to_int64()
}
///|
/// Load 16-bit signed integer, sign-extend to i64
pub fn Memory::load_i64_16s_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
let v = self.load_i32_16s_at(addr)
v.to_int64()
}
///|
/// Load 16-bit unsigned integer, zero-extend to i64
pub fn Memory::load_i64_16u_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
let v = self.load_i32_16u_at(addr)
v.to_int64()
}
///|
/// Load 32-bit signed integer, sign-extend to i64
pub fn Memory::load_i64_32s_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
self.load_i32_at(addr).to_int64()
}
///|
/// Load 32-bit unsigned integer, zero-extend to i64
pub fn Memory::load_i64_32u_at(
self : Memory,
addr : Int64,
) -> Int64 raise RuntimeError {
let value = self.load_i32_at(addr)
value.reinterpret_as_uint().to_uint64().reinterpret_as_int64()
}
///|
/// Store low 8 bits of i32
pub fn Memory::store_i32_8_at(
self : Memory,
addr : Int64,
value : Int32,
) -> Unit raise RuntimeError {
self.store_byte_at(addr, value.land(0xFF).to_byte())
}
///|
/// Store low 16 bits of i32
pub fn Memory::store_i32_16_at(
self : Memory,
addr : Int64,
value : Int32,
) -> Unit raise RuntimeError {
self.check_bounds_at(addr, 2)
let data = FixedArray::make(2, b'\x00')
data[0] = value.land(0xFF).to_byte()
data[1] = (value >> 8).land(0xFF).to_byte()
self.write_fixed_at(addr, data, 2)
}
///|
/// Store low 8 bits of i64
pub fn Memory::store_i64_8_at(
self : Memory,
addr : Int64,
value : Int64,
) -> Unit raise RuntimeError {
self.store_byte_at(addr, value.land(0xFFL).to_int().to_byte())
}
///|
/// Store low 16 bits of i64
pub fn Memory::store_i64_16_at(
self : Memory,
addr : Int64,
value : Int64,
) -> Unit raise RuntimeError {
self.check_bounds_at(addr, 2)
let data = FixedArray::make(2, b'\x00')
data[0] = value.land(0xFFL).to_int().to_byte()
data[1] = (value >> 8).land(0xFFL).to_int().to_byte()
self.write_fixed_at(addr, data, 2)
}
///|
/// Store low 32 bits of i64
pub fn Memory::store_i64_32_at(
self : Memory,
addr : Int64,
value : Int64,
) -> Unit raise RuntimeError {
self.store_i32_at(addr, value.to_int())
}
///|
pub fn Memory::init_data_at(
self : Memory,
destination : Int64,
data : Bytes,
) -> Unit raise RuntimeError {
self.check_bounds_at(destination, data.length().to_int64())
if data.length() > 0 {
self.write_bytes_unchecked_at(destination, data)
}
}
///|
/// Copy bytes after checking both ranges, including zero-length copies.
/// Native memmove also handles aliases imported under different memory indices.
pub fn Memory::copy_from(
self : Memory,
destination : Int64,
source : Memory,
source_offset : Int64,
length : Int64,
) -> Unit raise RuntimeError {
self.check_bounds_at(destination, length)
source.check_bounds_at(source_offset, length)
if length > 0L {
self.copy_unchecked_at(destination, source, source_offset, length)
}
}
///|
pub fn Memory::copy_at(
self : Memory,
destination : Int64,
source : Int64,
length : Int64,
) -> Unit raise RuntimeError {
self.copy_from(destination, self, source, length)
}
///|
pub fn Memory::fill_at(
self : Memory,
destination : Int64,
value : Byte,
length : Int64,
) -> Unit raise RuntimeError {
self.check_bounds_at(destination, length)
if length > 0L {
self.fill_unchecked_at(destination, value, length)
}
}