Kernel scratch allocation¶
A bump cursor makes construction order and lifetime visible in the Sail semantics while the bytes themselves remain in host-backed memory.
Non-normative
This page documents the model's host interface — internal contracts of the executable specification, not protocol rules.
register scratch_arena¶
The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICElet EMPTY_SCRATCH_SLICE : ScratchSliceFields(0, 0) = scratch_slice(0, 0)A scratch-arena range with its coordinate and length packed existentially.
type ScratchSlice = {
'off 'len,
scratch_valid_range('off, 'len).
ScratchSliceFields('off, 'len)
}function scratch_length_add¶
function scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
assert(false, "scratch length overflow");
0
}function scratch_length_add(left, right) =
if right <= sizeof(scratch_region_bound) - left then {
left + right
} else {
assert(false, "scratch length overflow");
0
}Executor scratch-arena capacity.
type scratch_region_bound : Int = default_host_region_boundfunction scratch_begin¶
Marks the start of a scratch construction.
function scratch_begin() -> source_pointer = {
let arena = scratch_arena;
arena.len
}The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEAn absolute byte position in a named source region.
type source_pointer = range(0, default_host_region_bound)function scratch_reserve¶
function scratch_reserve(len) = {
let arena = scratch_arena;
let reserved = host_scratch_reserve(arena.len, len);
assert(reserved, "scratch reserve");
arena.len
}Ensures that len bytes can be appended at the current explicit arena
offset without changing the Sail-owned high-water mark.
val host_scratch_reserve = impure { c: "scratch_reserve_at" } : forall 'off 'len,
0
<= 'off
& 'off
<= scratch_region_bound
& 0
<= 'len
& 'len
<= scratch_region_bound. (int('off), int('len)) -> boolfunction scratch_reserve(len) = {
let arena = scratch_arena;
let reserved = host_scratch_reserve(arena.len, len);
assert(reserved, "scratch reserve");
arena.len
}The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEfunction scratch_push_byte¶
Appends one byte without constructing a Sail list.
function scratch_push_byte(data : byte) -> unit = {
let arena = scratch_arena;
scratch_arena = host_scratch_store_byte(arena.len, data)
}Stores one byte at an explicit arena offset.
val host_scratch_store_byte = impure { c: "scratch_store_byte" } : forall 'off,
0 <= 'off & 'off <= scratch_region_bound. (
int('off),
byte,
) -> ScratchSliceLength('off + 1)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEAn 8-bit byte.
type byte = bits(8)function stateless_input_scratch_push_slice¶
Appends a source-backed slice at the cursor.
function stateless_input_scratch_push_slice(data : StatelessInputSlice) -> unit = {
if data.len != 0 then {
let arena = scratch_arena;
scratch_arena = host_scratch_store_stateless_input(arena.len, data)
}
}Stores a source-backed slice at an explicit arena offset. Success carries the resulting arena high-water mark.
val host_scratch_store_stateless_input = impure { c: "scratch_store_stateless_input" } : forall 'dst 'off 'len,
0
<= 'dst
& 'dst
<= scratch_region_bound
& stateless_input_valid_range(
'off,
'len,
). (
int('dst),
StatelessInputSliceFields('off, 'len),
) -> ScratchSliceLength('dst + 'len)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA stateless-input range with its coordinate and length packed existentially.
type StatelessInputSlice = {
'off 'len,
stateless_input_valid_range('off, 'len).
StatelessInputSliceFields('off, 'len)
}function scratch_scratch_push_slice¶
Appends an existing arena-backed slice at the cursor.
function scratch_scratch_push_slice(data : ScratchSlice) -> unit = {
if data.len != 0 then {
let arena = scratch_arena;
scratch_arena = host_scratch_store_scratch(arena.len, data)
}
}Stores an existing arena-backed slice at an explicit arena offset; the result carries the new arena high-water mark.
val host_scratch_store_scratch = impure { c: "scratch_store_scratch" } : forall 'dst 'off 'len,
0
<= 'dst
& 'dst
<= scratch_region_bound
& scratch_valid_range('off, 'len). (
int('dst),
ScratchSliceFields('off, 'len),
) -> ScratchSliceLength('dst + 'len)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA scratch-arena range with its coordinate and length packed existentially.
type ScratchSlice = {
'off 'len,
scratch_valid_range('off, 'len).
ScratchSliceFields('off, 'len)
}function log_data_scratch_push_slice¶
Appends a retained-log-data slice at the cursor.
function log_data_scratch_push_slice(data : LogDataSlice) -> unit = {
if data.len != 0 then {
let arena = scratch_arena;
scratch_arena = host_scratch_store_log_data(arena.len, data)
}
}Stores a retained-log-data slice at an explicit arena offset; the result carries the new arena high-water mark.
val host_scratch_store_log_data = impure { c: "scratch_store_log_data" } : forall 'dst 'off 'len,
0
<= 'dst
& 'dst
<= scratch_region_bound
& log_data_valid_range('off, 'len). (
int('dst),
LogDataSliceFields('off, 'len),
) -> ScratchSliceLength('dst + 'len)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA log-data range with its coordinate and length packed existentially.
type LogDataSlice = {
'off 'len,
log_data_valid_range('off, 'len).
LogDataSliceFields('off, 'len)
}function output_scratch_push_slice¶
Appends an output-buffer-backed slice at the cursor.
function output_scratch_push_slice(data : OutputSlice) -> unit = {
if data.len != 0 then {
let arena = scratch_arena;
scratch_arena = host_scratch_store_output(arena.len, data)
}
}Stores an output-buffer-backed slice at an explicit arena offset; the result carries the new arena high-water mark.
val host_scratch_store_output = impure { c: "scratch_store_output" } : forall 'dst 'off 'len,
0
<= 'dst
& 'dst
<= scratch_region_bound
& output_region_valid_range('off, 'len). (
int('dst),
OutputSliceFields('off, 'len),
) -> ScratchSliceLength('dst + 'len)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA frame-output range with its coordinate and length packed existentially.
type OutputSlice = {
'off 'len,
output_region_valid_range('off, 'len).
OutputSliceFields('off, 'len)
}function scratch_push_address¶
Appends a fixed 20-byte address at the cursor.
function scratch_push_address(data : address) -> unit = {
let arena = scratch_arena;
scratch_arena = host_scratch_store_address(arena.len, data)
}Stores a fixed 20-byte address in wire order.
val host_scratch_store_address = impure { c: "scratch_store_address" } : forall 'off,
0 <= 'off & 'off <= scratch_region_bound. (
int('off),
address,
) -> ScratchSliceLength('off + 20)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA 20-byte account address (YP §4.1), in canonical protocol byte order.
type address = vector(20, inc, byte)function scratch_push_b256¶
Appends a prefix of a fixed 32-byte value at the cursor.
function scratch_push_b256(data : b256, len : range(0, 32)) -> unit = {
if len != 0 then {
let arena = scratch_arena;
scratch_arena = host_scratch_store_b256(arena.len, data, len)
}
}Stores the first len wire-order bytes of a fixed 32-byte value.
val host_scratch_store_b256 = impure { c: "scratch_store_b256" } : forall 'off 'len,
0
<= 'off
& 'off
<= scratch_region_bound
& 0
<= 'len
& 'len
<= 32. (int('off), b256, int('len)) -> ScratchSliceLength(
'off + 'len,
)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA KECCAK-256 / storage-key sized digest, in canonical protocol byte order.
type b256 = vector(32, inc, byte)function scratch_push_fixed_bytes_256¶
Appends a decreasing fixed 256-byte vector in canonical wire order.
function scratch_push_fixed_bytes_256(data : vector(256, dec, byte)) -> unit = {
let arena = scratch_arena;
scratch_arena = host_scratch_store_fixed_bytes_256(arena.len, data)
}Stores a decreasing fixed 256-byte vector in canonical wire order.
val host_scratch_store_fixed_bytes_256 = impure { c: "scratch_store_fixed_bytes_256" } : forall 'off,
0
<= 'off
& 'off
<= scratch_region_bound. (
int('off),
vector(256, dec, byte),
) -> ScratchSliceLength('off + 256)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEAn 8-bit byte.
type byte = bits(8)function scratch_push_word_be¶
Appends the low len bytes of a word in canonical big-endian order.
function scratch_push_word_be(data : word, len : range(0, 32)) -> unit = {
if len != 0 then {
let arena = scratch_arena;
scratch_arena = host_scratch_store_word(arena.len, data, len)
}
}Stores the low len bytes of a word in canonical big-endian order.
val host_scratch_store_word = impure { c: "scratch_store_word" } : forall 'off 'len,
0
<= 'off
& 'off
<= scratch_region_bound
& 0
<= 'len
& 'len
<= 32. (int('off), word, int('len)) -> ScratchSliceLength(
'off + 'len,
)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEThe EVM 256-bit machine word (YP §9.1). A transparent range keeps the mathematical subtype relation visible: narrower non-negative ranges can be passed as words without a model-level conversion.
type word = range(0, 2 ^ 256 - 1)function scratch_finish¶
The slice covering everything pushed since start.
function scratch_finish(start : source_pointer) -> ScratchSlice =
let start_offset = start in
let arena = scratch_arena in
let stop_offset = arena.len in
if start_offset <= stop_offset then {
sub_slice(arena, start, stop_offset - start_offset)
} else {
assert(false, "scratch finish mark");
EMPTY_SCRATCH_SLICE
}let EMPTY_SCRATCH_SLICE : ScratchSliceFields(0, 0) = scratch_slice(0, 0)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEA scratch-arena range with its coordinate and length packed existentially.
type ScratchSlice = {
'off 'len,
scratch_valid_range('off, 'len).
ScratchSliceFields('off, 'len)
}An absolute byte position in a named source region.
type source_pointer = range(0, default_host_region_bound)function scratch_rewind¶
Discards everything pushed since mark.
function scratch_rewind(mark : source_pointer) -> unit =
let mark_offset = mark in
let arena = scratch_arena in
let cursor_offset = arena.len in
if mark_offset <= cursor_offset then {
scratch_arena = sub_slice(arena, 0, mark);
host_scratch_truncate(mark)
} else {
assert(false, "scratch rewind mark")
}Discards arena contents from the given offset on; the axiom behind scratch_rewind and scratch_reset.
val host_scratch_truncate = impure { c: "scratch_truncate" } : scratch_pointer -> unitThe active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICEAn absolute byte position in a named source region.
type source_pointer = range(0, default_host_region_bound)function scratch_reset¶
Empties the arena (per-block lifetime).
function scratch_reset() -> unit = {
scratch_arena = EMPTY_SCRATCH_SLICE;
host_scratch_truncate(0)
}Discards arena contents from the given offset on; the axiom behind scratch_rewind and scratch_reset.
val host_scratch_truncate = impure { c: "scratch_truncate" } : scratch_pointer -> unitlet EMPTY_SCRATCH_SLICE : ScratchSliceFields(0, 0) = scratch_slice(0, 0)The active scratch arena; its length is the Sail-owned high-water mark.
register scratch_arena : ScratchSlice = EMPTY_SCRATCH_SLICE