Arena structv0.1.0
pub struct Arena {
// private fields
}
Move-only, because it owns its blocks and explicitly prohibits copying. Pass its Handle where a storable
Allocator adapter is needed.
Constructors
Arena
pub func Arena(backing: Allocator, blockSize: uint) -> Arena
An arena that will take its storage from backing.
backing is borrowed for the call and kept. blockSize is the payload of the first block; zero asks for the
default. Cannot fail and allocates nothing: the first block is taken when the first request arrives, so an
arena that is never used costs nothing.
| Name | Type | Description |
|---|---|---|
backing | Allocator | where the arena's blocks come from |
blockSize | uint | the first block's payload in bytes; zero asks for the default |
Returns: the arena, holding no block yet
Methods
Handle
pub func Handle(self: *var Arena) -> ArenaHandle
A copyable interface adapter pointing at this arena.
self stays the caller's. Allocations through the handle update this arena rather than copied state.
Cannot fail. Runs in constant time and allocates nothing.
The handle borrows the arena and must not outlive it, nor survive the arena being moved. Nothing here tracks
that: a handle held past its arena's life points at released storage.
Returns: a copyable adapter pointing at this arena
Reset
pub func Reset(self: &var Arena)
Rewinds every allocation, keeping the largest block for the next round of work.
self is borrowed mutably. Every pointer the arena ever handed out becomes invalid, including any still held
by a caller — an arena reclaims in one operation, which is what makes it fast and what makes this the one
dangerous call on it.
The kept block is the current one, which is the largest, so a second round of the same work runs without
asking the system for anything. Runs in time proportional to the number of blocks.
Nothing may be read from or written to any earlier allocation afterwards.
BytesUsed
pub func BytesUsed(self: &Arena) -> uint
How many payload bytes the arena has handed out, summed over every block it holds.
self is borrowed. Alignment padding between allocations counts as used. The tail a block was left with when
a request did not fit in it and the arena moved on to a new block is not counted: nothing was handed out of
it, and nothing will be until a Reset. Runs in time proportional to the number of blocks and allocates
nothing.
Returns: how many payload bytes the arena's blocks have handed out
BlockCount
pub func BlockCount(self: &Arena) -> uint
How many blocks the arena holds.
self is borrowed. Zero before the first request.
Returns: how many blocks the arena holds
Destructor
func ~Arena(self: &var Arena)
Gives every block back to the backing allocator. Runs when the arena's life ends. Every pointer the arena handed out becomes invalid.
Related
DefaultArenaBlockSize
pub const DefaultArenaBlockSize: uint = 4096;
The default payload of an arena's first block. One page on every supported system. Each later block doubles, so an arena that keeps growing stops asking the system after a handful of requests.
ArenaBlockAlignment
pub const ArenaBlockAlignment: uint = 16;
The alignment every arena block is taken at, and so the strictest alignment an arena can serve from one.
ArenaBlock
pub struct ArenaBlock {
// private fields
}
One block of arena storage, with the bookkeeping for what has been handed out of it.
Never constructed by a caller. The payload follows the header immediately, and capacity counts only the
payload.
ArenaHandle
pub struct ArenaHandle {
// private fields
}
A copyable interface adapter pointing at an arena. A handle is cheap to copy and may be stored wherever an allocator is wanted, but it does not own the arena.
Implements Allocator
ArenaHandle conforms to Allocator.
Allocate
pub func Allocate(
self: &ArenaHandle,
layout: Layout
) -> (*var opaque) ! AllocError
Takes storage matching layout by advancing the arena's pointer.
result receives the address when this reports None. A zero-sized layout gives the shared empty block and
consumes none of the arena.
Takes a new block when the current one cannot serve the request, which is the only path here that can fail.
The storage lives until the arena is reset or destroyed, whichever comes first, and not one moment longer.
| Name | Type | Description |
|---|---|---|
layout | Layout | the size and alignment wanted |
Returns: the address of the storage, or why none was taken
Deallocate
pub func Deallocate(
self: &ArenaHandle,
block: *var opaque,
layout: Layout
) -> ! AllocError
Accepts a release and reclaims only what it can.
An arena reclaims in one operation, so an ordinary release has nothing to give back and reports None having
done nothing. The exception is the most recent allocation, whose space is rewound — which is what lets a
growing collection in an arena not waste every earlier size it passed through.
Accepting rather than refusing is deliberate: it lets an arena stand in for any allocator in generic code
that releases what it takes.
| Name | Type | Description |
|---|---|---|
block | *var opaque | an address this allocator returned |
layout | Layout | the layout it was allocated with |
Returns: nothing when the release was accepted, or why it was refused
Reallocate
pub func Reallocate(
self: &ArenaHandle,
block: *var opaque,
oldLayout: Layout,
newLayout: Layout
) -> (*var opaque) ! AllocError
Grows or shrinks a block, extending the most recent allocation in place where it can. The two layouts must share an alignment. Growing the most recent allocation extends it without moving when the current block has room; anything else takes new storage and copies, leaving the old space behind for the next reset to reclaim. Shrinking to a zero-sized layout releases the block and reports the shared empty address rather than the one it rewound off of, so the result is releasable with the layout it was asked for.
| Name | Type | Description |
|---|---|---|
block | *var opaque | the existing allocation's address |
oldLayout | Layout | the layout block was allocated with |
newLayout | Layout | the layout wanted, which must have the same alignment |
Returns: the address the block is at afterwards, or why it was not resized