Module arena

arena
Stability: stable — changes additively from here.

A bump arena: an explicit Allocator over one region.

{ Arena } :: import("std/arena");
arena := Arena.new(usize(1024) * usize(1024));
a := arena.allocator();
// … hand `a` to anything that takes an Allocator …
arena.deinit();   // PANICS if a block is still live

Placement, not lifetime: under reference counting every block still dies when its last reference goes away, and its release is routed back here by the owner prefix (std/allocator.yo). What the arena adds is where the bytes live — one contiguous region — and a checked end: deinit (and the arena handle's own dispose) panics while any block is live, so "the arena died under a live block" is a loud, defined failure instead of the use-after-free it is in Zig. A process-lifetime arena calls abandon() instead: tracking stops and the region is never released.

Freeing a block reclaims its bytes only when it is the most recent allocation; everything else is reclaimed by deinit. realloc grows the most recent block in place when the region has room.

Thread-safe: every operation takes the arena's spinlock, so blocks may be freed on any thread.

Under --debug-heap the exit report lists every arena still live (never deinit) or abandoned at exit, with its live blocks and bytes in use.

Stability

stable — new, allocator, scoped, deinit, abandon, live_blocks, used_bytes, capacity and is_released, frozen with the Allocator vtable (plans/archive/EXPLICIT_ALLOCATORS.md).

Types

Arena object
Arena

A bump arena over one region. See the module notes.

Fields

NameTypeDescription
_state*(_ArenaState)

Trait Implementations

impl(Arena, ...)
new : (Arena) fn(capacity : usize) -> Arena

An arena over a region of capacity bytes (rounded up to 16). Panics when the region cannot be allocated.

Parameters

NameTypeNotesDescription
capacityusize

The region's size in bytes.

Returns: Arena

allocator : (Arena) fn(self : Arena) -> Allocator

This arena as an Allocator value. Every block it hands out is released back here, wherever and whenever its last reference dies.

Parameters

NameTypeNotes
selfArena

Returns: Allocator

scoped : (Arena) fn(generic(T) self : Arena, f : Impl(Fn() -> T)) -> T

with_allocator(self.allocator(), f): evaluate f() with this arena as the current allocator.

Parameters

NameTypeNotes
selfArena
fImpl(Fn() -> T)

Returns: T

live_blocks : (Arena) fn(self : Arena) -> usize

Blocks allocated here and not yet freed.

Parameters

NameTypeNotes
selfArena

Returns: usize

used_bytes : (Arena) fn(self : Arena) -> usize

Bytes of the region in use (the bump offset).

Parameters

NameTypeNotes
selfArena

Returns: usize

capacity : (Arena) fn(self : Arena) -> usize

The region's size in bytes.

Parameters

NameTypeNotes
selfArena

Returns: usize

is_released : (Arena) fn(self : Arena) -> bool

Whether deinit or abandon has run.

Parameters

NameTypeNotes
selfArena

Returns: bool

deinit : (Arena) fn(self : Arena) -> unit

Release the region. PANICS if any block is still live. A second call, or a call after abandon, is a no-op. Also runs when the last Arena handle dies.

Parameters

NameTypeNotes
selfArena

Returns: unit

abandon : (Arena) fn(self : Arena) -> unit

Stop tracking and never release the region: for an arena that lives as long as the process (startup tables, interners). Blocks may still be freed into it; deinit becomes a no-op.

Parameters

NameTypeNotes
selfArena

Returns: unit

impl(Arena, Dispose(...))
dispose : (Arena) fn(self : Arena) -> unit

Release the resources self owns — a file descriptor, a socket, a lock, a buffer the allocator handed out. Called automatically when the value's owner drops it (a value type) or the last reference to it goes away (a reference type), so an implementor never calls it directly and must tolerate being the only one who ever does.

It must be safe to run exactly once, and a type that also exposes an explicit close/release is responsible for making the second call a no-op.

Parameters

NameTypeNotes
selfArena

Returns: unit