Skip to main content

Arena

Struct Arena 

Source
pub struct Arena { /* private fields */ }
Expand description

A dual-end bump-allocated arena.

Owns or borrows a fixed-size buffer of bytes. Two bump pointers track allocation positions at each end. The bottom end grows from low addresses upward. The top end grows from high addresses downward. Allocation fails when the two pointers would meet.

The arena is not the program’s #[global_allocator] and is not intended to be one. It is designed for scoped per-region or per-thread use through BottomHandle and TopHandle, which the host passes to allocator-aware collection constructors. The standard global allocator continues to handle every allocation that does not route through an arena handle. Hosts that want every allocation in the program to be arena-backed must wrap the arena in a thread-safe allocator and install it via #[global_allocator]; this crate does not provide such a wrapper because doing so well requires choices that depend on the host’s threading and synchronization model.

§Generations and stale-pointer detection

The arena carries an epoch counter that increments on Arena::reset. The ArenaHandle family of safe wrappers captures the epoch at construction and validates it on access, returning Stale if the arena has been reset since the handle was issued. The counter is u64 and uses checked arithmetic. A saturated counter halts the arena’s reset path with EpochSaturated. Saturation requires roughly five hundred eighty four thousand years at one reset per microsecond and is documentation rather than a real failure mode in expected use.

In-process recovery from saturation is possible through Arena::force_reset_epoch, which is unsafe and requires the caller to certify that no ArenaHandle from any prior epoch is reachable. Cross-process recovery for very long-lived deployments uses checkpoint and restart against host-owned non-volatile storage. ArenaHandle is intentionally not serializable because its pointer is not stable across processes.

See the crate-level documentation for the design overview.

Implementations§

Source§

impl Arena

Source

pub fn with_capacity(capacity: usize) -> Arena

Create an arena backed by a freshly allocated heap buffer of the given byte capacity.

Available only with the alloc feature. The buffer is zeroed at construction and is allocated with 16-byte alignment, which covers the alignment requirements of i64, f64, u128, and most platform-native pointers and primitives.

Panics on allocation failure via the standard handle_alloc_error path. A capacity of zero produces an arena that satisfies allocation requests for zero-size layouts only; non-zero allocations return AllocError.

§Examples
use allocator_api2::vec::Vec as ArenaVec;
use keleusma_arena::Arena;

let arena = Arena::with_capacity(1024);
let mut v: ArenaVec<i64, _> = ArenaVec::new_in(arena.stack_handle());
v.push(1);
v.push(2);
v.push(3);
assert_eq!(v.len(), 3);
assert!(arena.bottom_used() >= 24);
Examples found in repository?
examples/generic_match.rs (line 16)
12fn run(label: &str, src: &str) -> Value {
13    let tokens = tokenize(src).expect("lex");
14    let program = parse(&tokens).expect("parse");
15    let module = compile(&program).expect("compile");
16    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
17    let mut vm = Vm::new(module, &arena).expect("verify");
18    match vm.call(&[]) {
19        Ok(VmState::Finished(v)) => {
20            println!("{}: {:?}", label, v);
21            v
22        }
23        other => panic!("{}: {:?}", label, other),
24    }
25}
More examples
Hide additional examples
examples/target_aware_compile.rs (line 90)
86fn compile_and_run(src: &str, target: &Target) {
87    let tokens = tokenize(src).expect("lex");
88    let program = parse(&tokens).expect("parse");
89    let module = compile_with_target(&program, target).expect("compile");
90    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
91    let mut vm = Vm::new(module, &arena).expect("verify");
92    match vm.call(&[]).expect("call") {
93        VmState::Finished(v) => println!("  result: {:?}", v),
94        other => panic!("unexpected: {:?}", other),
95    }
96    let _ = Value::Unit;
97}
examples/generic_identity.rs (line 27)
19fn main() {
20    let src = r#"
21        fn id<T>(x: T) -> T { x }
22        fn main() -> i64 { id(42) }
23    "#;
24    let tokens = tokenize(src).expect("lex");
25    let program = parse(&tokens).expect("parse");
26    let module = compile(&program).expect("compile");
27    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
28    let mut vm = Vm::new(module, &arena).expect("verify");
29    match vm.call(&[]) {
30        Ok(VmState::Finished(Value::Int(n))) => {
31            println!("id(42) = {}", n);
32            assert_eq!(n, 42);
33            println!("generic identity executed end to end");
34        }
35        other => panic!("unexpected: {:?}", other),
36    }
37}
examples/generic_struct.rs (line 28)
17fn main() {
18    let src = r#"
19        struct Cell<T> { value: T }
20        fn main() -> i64 {
21            let c = Cell { value: 42 };
22            c.value
23        }
24    "#;
25    let tokens = tokenize(src).expect("lex");
26    let program = parse(&tokens).expect("parse");
27    let module = compile(&program).expect("compile");
28    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
29    let mut vm = Vm::new(module, &arena).expect("verify");
30    match vm.call(&[]) {
31        Ok(VmState::Finished(Value::Int(n))) => {
32            println!("Cell {{ value: 42 }}.value = {}", n);
33            assert_eq!(n, 42);
34            println!("generic struct executed end to end");
35        }
36        other => panic!("unexpected: {:?}", other),
37    }
38}
examples/method_call.rs (line 32)
20fn main() {
21    let src = r#"
22        trait Doubler { fn double(x: i64) -> i64; }
23        impl Doubler for i64 { fn double(x: i64) -> i64 { x + x } }
24        fn main() -> i64 {
25            let n: i64 = 21;
26            n.double()
27        }
28    "#;
29    let tokens = tokenize(src).expect("lex");
30    let program = parse(&tokens).expect("parse");
31    let module = compile(&program).expect("compile");
32    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
33    let mut vm = Vm::new(module, &arena).expect("verify");
34    match vm.call(&[]) {
35        Ok(VmState::Finished(Value::Int(n))) => {
36            println!("21.double() = {}", n);
37            assert_eq!(n, 42);
38            println!("method call dispatch executed end to end");
39        }
40        other => panic!("unexpected: {:?}", other),
41    }
42}
examples/monomorphize_generic_method.rs (line 31)
21fn main() {
22    let src = r#"
23        trait Doubler { fn double(x: i64) -> i64; }
24        impl Doubler for i64 { fn double(x: i64) -> i64 { x + x } }
25        fn use_doubler<T: Doubler>(x: T) -> i64 { x.double() }
26        fn main() -> i64 { use_doubler(21) }
27    "#;
28    let tokens = tokenize(src).expect("lex");
29    let program = parse(&tokens).expect("parse");
30    let module = compile(&program).expect("compile");
31    let arena = Arena::with_capacity(DEFAULT_ARENA_CAPACITY);
32    let mut vm = Vm::new(module, &arena).expect("verify");
33    match vm.call(&[]) {
34        Ok(VmState::Finished(Value::Int(n))) => {
35            println!("use_doubler(21) = {}", n);
36            assert_eq!(n, 42);
37            println!("monomorphization-driven method dispatch executed end to end");
38        }
39        other => panic!("unexpected: {:?}", other),
40    }
41}
Source

pub fn from_static_buffer(buffer: &'static mut [u8]) -> Arena

Create an arena backed by a static buffer.

The buffer must outlive the arena. The 'static mut requirement satisfies this for typical embedded patterns where the buffer is a static array placed in BSS or DATA. For shorter-lived buffers, see Arena::from_buffer_unchecked.

Source

pub unsafe fn from_buffer_unchecked(ptr: *mut u8, capacity: usize) -> Arena

Create an arena from a raw pointer and length.

The buffer’s base alignment does not need to match the alignment of any particular allocation type. The arena computes alignment against the actual buffer base address and pads as needed for each aligned allocation.

§Safety

The caller must uphold the following.

  • ptr is non-null.
  • ptr is valid for reads and writes of capacity bytes for the entire lifetime of the returned arena.
  • No other code accesses the buffer through any path that would alias with the arena’s allocations during the arena’s lifetime.

This constructor is the only path that admits buffers with non-'static lifetimes. It exists for embedded contexts where the lifetime is known statically through other means but the type system cannot express it. Most callers should prefer Arena::from_static_buffer.

Source

pub fn capacity(&self) -> usize

Total capacity of the arena in bytes.

Examples found in repository?
examples/wcmu_basic.rs (line 46)
23fn main() {
24    // Compile.
25    let tokens = tokenize(SCRIPT).expect("lex error");
26    let program = parse(&tokens).expect("parse error");
27    let module = compile(&program).expect("compile error");
28
29    // Inspect the WCMU budget for each Stream chunk in the module.
30    let chunk_wcmu = verify::module_wcmu(&module, &[]).expect("module wcmu");
31    for (idx, chunk) in module.chunks.iter().enumerate() {
32        if matches!(chunk.block_type, keleusma::bytecode::BlockType::Stream) {
33            let (stack_bytes, heap_bytes) = chunk_wcmu[idx];
34            println!("chunk `{}`:", chunk.name);
35            println!("  stack WCMU: {} bytes", stack_bytes);
36            println!("  heap  WCMU: {} bytes", heap_bytes);
37            println!("  total:      {} bytes", stack_bytes + heap_bytes);
38        }
39    }
40
41    // Construct an auto-sized arena and a Vm that borrows it.
42    let cap = keleusma::vm::auto_arena_capacity_for(&module, &[]).expect("auto capacity");
43    let arena = keleusma::Arena::with_capacity(cap);
44    let mut vm = Vm::new(module, &arena).expect("vm construction");
45    println!();
46    println!("auto-sized arena capacity: {} bytes", vm.arena().capacity());
47
48    // Drive the coroutine through one yield.
49    match vm.call(&[Value::Int(21)]).expect("vm call") {
50        VmState::Yielded(v) => println!("yielded: {:?}", v),
51        other => panic!("expected yield, got {:?}", other),
52    }
53}
More examples
Hide additional examples
examples/wcmu_attestation.rs (line 70)
26fn main() {
27    // Compile.
28    let tokens = tokenize(SCRIPT).expect("lex error");
29    let program = parse(&tokens).expect("parse error");
30    let module = compile(&program).expect("compile error");
31
32    // Inspect the WCMU budget with no native attestation. Heap is zero.
33    let chunk_wcmu_default = verify::module_wcmu(&module, &[]).expect("module wcmu");
34    println!("Default attestation (no host declaration):");
35    for (idx, chunk) in module.chunks.iter().enumerate() {
36        if matches!(chunk.block_type, keleusma::bytecode::BlockType::Stream) {
37            let (s, h) = chunk_wcmu_default[idx];
38            println!("  chunk `{}`: stack {} heap {}", chunk.name, s, h);
39        }
40    }
41
42    // Inspect with the host's declared attestation. Heap reflects the
43    // bound the host promises the native will not exceed.
44    let attested_native_wcmu = [256u32];
45    let chunk_wcmu_attested =
46        verify::module_wcmu(&module, &attested_native_wcmu).expect("module wcmu");
47    println!();
48    println!("Attested host::compute_value with 256 bytes:");
49    for (idx, chunk) in module.chunks.iter().enumerate() {
50        if matches!(chunk.block_type, keleusma::bytecode::BlockType::Stream) {
51            let (s, h) = chunk_wcmu_attested[idx];
52            println!("  chunk `{}`: stack {} heap {}", chunk.name, s, h);
53        }
54    }
55
56    // Construct a Vm with adequate capacity.
57    let arena = keleusma::Arena::with_capacity(4096);
58    let mut vm = Vm::new(module, &arena).expect("vm construction");
59
60    // Register the native and declare its WCET and WCMU bounds.
61    vm.register_fn("host::compute_value", |x: i64| -> i64 { x * 3 });
62    vm.set_native_bounds("host::compute_value", 25, 256)
63        .expect("set bounds");
64
65    // Re-verify resources with the declared attestations now in place.
66    vm.verify_resources().expect("verify_resources");
67    println!();
68    println!(
69        "verify_resources succeeded with arena {} bytes",
70        vm.arena().capacity()
71    );
72
73    // Drive the coroutine through one yield.
74    match vm.call(&[Value::Int(7)]).expect("vm call") {
75        VmState::Yielded(v) => println!("yielded: {:?}", v),
76        other => panic!("expected yield, got {:?}", other),
77    }
78}
Source

pub fn bottom_used(&self) -> usize

Bytes currently allocated from the bottom end.

Source

pub fn top_used(&self) -> usize

Bytes currently allocated from the top end.

Source

pub fn free(&self) -> usize

Bytes available for either end to consume.

Source

pub fn bottom_peak(&self) -> usize

Highest observed bottom usage in bytes since arena creation or the most recent Arena::clear_peaks call.

Source

pub fn top_peak(&self) -> usize

Highest observed top usage in bytes since arena creation or the most recent Arena::clear_peaks call.

Source

pub fn bottom_mark(&self) -> BottomMark

Return a snapshot of the bottom-end bump pointer for later use with Arena::rewind_bottom.

Source

pub fn top_mark(&self) -> TopMark

Return a snapshot of the top-end bump pointer for later use with Arena::rewind_top.

Source

pub fn reset(&mut self) -> Result<(), EpochSaturated>

Reset both ends, reclaiming all allocations.

Constant-time. Does not zero the buffer contents because subsequent allocations will overwrite as needed. Does not clear peak watermarks; use Arena::clear_peaks for that.

Advances the epoch counter, invalidating every outstanding ArenaHandle. Returns EpochSaturated if the counter is already at u64::MAX. See Arena::force_reset_epoch for recovery.

Takes &mut self so the borrow checker prevents calling reset while any handle borrows the arena. This guarantees no live allocations through Allocator trait users at the moment of reset.

Source

pub unsafe fn reset_unchecked(&self) -> Result<(), EpochSaturated>

Reset both ends and advance the epoch through a shared reference.

Companion to Arena::reset for callers that hold the arena through a shared reference and cannot temporarily acquire exclusive access. The interior-mutable bump pointers and epoch counter make the implementation race-free for single-threaded use.

§Safety

The caller must certify that no allocator-bound collection holds storage in the arena at the moment of reset. Concretely, no allocator_api2::vec::Vec<T, BottomHandle> or allocator_api2::vec::Vec<T, TopHandle> value may have non-zero capacity when this is called. Outstanding ArenaHandle values are correctly invalidated by the epoch advance and remain safe.

Returns EpochSaturated when the epoch counter is at u64::MAX. Recovery is via Arena::force_reset_epoch.

Source

pub unsafe fn reset_top_unchecked(&self) -> Result<(), EpochSaturated>

Reset the top end and advance the epoch through a shared reference, leaving the bottom end untouched.

Intended for hosts that use the bottom end for long-lived allocator-bound collections (such as an operand stack) while using the top end for short-lived scratch (such as dynamic strings). The epoch advance invalidates every outstanding ArenaHandle regardless of which end produced it. This is the desired discipline because handles do not record which end they came from and any handle that survives a reset is by definition stale.

§Safety

The caller must certify that no allocator-bound collection holds storage in the top end at the moment of reset. Bottom-end allocator-bound collections are unaffected by this call and retain their storage. Outstanding ArenaHandle values are correctly invalidated by the epoch advance and remain safe.

Returns EpochSaturated when the epoch counter is at u64::MAX. Recovery is via Arena::force_reset_epoch.

Source

pub fn epoch(&self) -> u64

Current epoch counter value.

Captured by ArenaHandle at construction and compared on access. Hosts performing long-running missions may consult this alongside Arena::epoch_remaining to schedule a graceful restart well before saturation.

Source

pub fn epoch_remaining(&self) -> u64

Number of resets remaining before the epoch counter saturates.

Source

pub unsafe fn force_reset_epoch(&mut self)

Reset the epoch counter to zero.

Recovery path for EpochSaturated. Resets bump pointers as well so the arena is in the same observable state as a freshly constructed arena, except for retained capacity.

§Safety

The caller must certify that no ArenaHandle produced under any prior epoch is reachable. Calling this while such handles exist invalidates the stale-detection guarantee and may permit use after invalidation that the type system would otherwise catch through epoch comparison.

The intended use is recovery after a Arena::reset call has returned EpochSaturated. The host halts every consumer of the arena, drains every cache that holds an ArenaHandle, and only then invokes this method.

Source

pub fn clear_peaks(&mut self)

Clear the peak watermarks for both ends.

Sets each peak to the current pointer value. After this call, peak readings reflect only allocations made after the call.

Source

pub unsafe fn rewind_bottom(&self, mark: BottomMark)

Rewind the bottom end to a previously recorded mark.

§Safety

The caller must ensure that no live values reference memory in the range [mark.0, current_bottom_top). References obtained through the Allocator trait, including those held by allocator_api2::vec::Vec and similar collections, must be dropped or otherwise abandoned before this call. Subsequent allocations may overwrite the rewound region, which would alias with any retained reference and produce undefined behavior.

Marks from a different arena are a logic error.

Source

pub unsafe fn rewind_top(&self, mark: TopMark)

Rewind the top end to a previously recorded mark.

§Safety

Same contract as Arena::rewind_bottom.

Source

pub unsafe fn reset_bottom(&self)

Clear the bottom end without checking for live references.

§Safety

The caller must ensure no live references into the bottom region exist. Equivalent to Arena::rewind_bottom with a mark of zero, with the same safety contract.

Source

pub unsafe fn reset_top(&self)

Clear the top end without checking for live references.

§Safety

The caller must ensure no live references into the top region exist. Equivalent to Arena::rewind_top with a mark of capacity, with the same safety contract.

Source

pub fn fits_budget(&self, budget: &Budget) -> bool

Returns true if the given budget fits within the arena’s capacity. The check is budget.bottom_bytes + budget.top_bytes <= capacity.

This is the generic budget contract referenced in the crate documentation. Producers compute a budget through whatever analysis they choose and use this method to verify admissibility before relying on the arena.

Source

pub fn bottom_handle(&self) -> BottomHandle<'_>

Obtain a bottom-end allocation handle.

Source

pub fn top_handle(&self) -> TopHandle<'_>

Obtain a top-end allocation handle.

Source

pub fn stack_handle(&self) -> BottomHandle<'_>

Alias for Arena::bottom_handle. Suitable for code that treats the bottom end as a stack-like region.

Source

pub fn heap_handle(&self) -> TopHandle<'_>

Alias for Arena::top_handle. Suitable for code that treats the top end as a heap-like region whose allocations are reset together rather than freed individually.

Source

pub fn alloc_bottom_bytes(&self, n: usize) -> Result<NonNull<[u8]>, AllocError>

Allocate n bytes from the bottom end with no alignment requirement. Convenience wrapper for byte buffers and similar allocations where the caller does not care about alignment.

Equivalent to allocating with a Layout::from_size_align(n, 1) through the BottomHandle Allocator implementation.

Source

pub fn alloc_top_bytes(&self, n: usize) -> Result<NonNull<[u8]>, AllocError>

Allocate n bytes from the top end with no alignment requirement.

Trait Implementations§

Source§

impl Debug for Arena

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Drop for Arena

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl !Freeze for Arena

§

impl !RefUnwindSafe for Arena

§

impl !Send for Arena

§

impl !Sync for Arena

§

impl Unpin for Arena

§

impl UnsafeUnpin for Arena

§

impl UnwindSafe for Arena

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> ArchivePointee for T

Source§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
Source§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> LayoutRaw for T

Source§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
Source§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

Source§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
Source§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
Source§

impl<T> Pointee for T

Source§

type Metadata = ()

The metadata type for pointers and references to this type.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.