// Memory and table size limits.
//
// Ported from `check_limits` in wax/src/lib-wax/typing.ml, which mirrors the
// wasm validator's own `limits` rule.
//
// The ceiling is not a constant: it is what the ADDRESS TYPE can index, divided
// by the page size. A 32-bit memory of one-byte pages tops out at 2^32 pages; of
// 64KiB pages, at 2^16. So the two immediates that look independent -- the
// address type and the page size -- decide the limit between them.
///|
/// The largest size a memory of this address type and page size can have.
pub fn max_memory_size(
address_type : @wasm_types.AddressType,
page_size_log2 : Int?,
) -> UInt64 {
let p = page_size_log2.unwrap_or(16)
let (bits, index_max) = match address_type {
I32 => (32, 0xFFFFFFFFUL)
I64 => (64, 0xFFFFFFFFFFFFFFFFUL)
}
let e = bits - p
let by_page = if e >= 64 {
index_max
} else if e <= 0 {
0UL
} else {
1UL << e
}
if index_max <= by_page {
index_max
} else {
by_page
}
}
///|
/// The largest size a table of this address type can have.
///
/// A table has no page size, so only the address type bounds it.
pub fn max_table_size(address_type : @wasm_types.AddressType) -> UInt64 {
match address_type {
I32 => 0xFFFFFFFFUL
I64 => 0xFFFFFFFFFFFFFFFFUL
}
}
///|
/// Validate one memory's or table's limits and page size.
///
/// `page_size_log2` is `None` for a table and for a memory that did not write
/// one; a memory that did may only say 1 byte or the default 64KiB, since those
/// are the two the proposal admits.
fn check_limits(
ctx : @typing_env.ModuleContext,
location : @basic.Location,
kind : String,
address_type : @wasm_types.AddressType,
limits : (UInt64, UInt64?)?,
page_size_log2? : Int? = None,
shared? : Bool = false,
) -> Unit {
match page_size_log2 {
None | Some(0) | Some(16) => ()
Some(_) => invalid_page_size(ctx.diagnostics, location)
}
// A shared memory is grown by other threads, so its size has to be bounded:
// without a maximum there is nothing for them to agree on.
if shared && !(limits is Some((_, Some(_)))) {
shared_memory_without_max(ctx.diagnostics, location)
}
guard limits is Some((mi, ma)) else { return }
let max = if kind == "memory" {
max_memory_size(address_type, page_size_log2)
} else {
max_table_size(address_type)
}
match ma {
None =>
if mi > max {
limit_too_large(ctx.diagnostics, location, kind, max)
}
Some(ma) => {
if mi > ma {
limit_mismatch(ctx.diagnostics, location, kind)
}
if ma > max {
limit_too_large(ctx.diagnostics, location, kind, max)
}
}
}
}
///|
/// A `UInt64` as lower-case hex.
///
/// Not `reinterpret_as_int64().to_string(radix=16)`: that renders the top half
/// of the range as a negative number, so a 64-bit memory's ceiling would print
/// as `0x-1`.
pub fn hex_u64(v : UInt64) -> String {
if v == 0 {
return "0"
}
let digits = "0123456789abcdef"
let out : Array[Char] = []
let mut n = v
while n > 0 {
out.push(digits.unsafe_get((n % 16UL).to_int()).to_char().unwrap())
n = n / 16UL
}
String::from_array(out.rev())
}