Memory and allocation
Allocator interfaces, scopes and raw allocation.
Contents
| Name | Summary |
|---|---|
Allocator | Everything a type must have to be used as an allocator: alloc and free, both static. |
Resizer | An allocator that can grow or shrink a block without moving it. |
Reallocator | An allocator that can grow or shrink a block, moving it if it has to. |
ZeroAllocator | An allocator that gets zeroed memory for free (fresh pages, calloc). |
ScopedAllocator | An allocator usable with AllocatorScope. |
FullAllocator | An allocator with every capability: Allocator plus resize, realloc and zeroed allocation. |
ScopedFullAllocator | A FullAllocator that is also a ScopedAllocator: what AllocatorScope takes. |
is_pow2 | Returns true when v is a power of two (zero is not). |
align_up | Rounds v up to the next multiple of align. |
dangling | Returns a non-null, aligned pointer that owns nothing: what a zero-size request gets. |
realloc_by_copy | A realloc for an allocator with no faster path: allocates, copies, frees the old block. |
set_oom_handler | Replaces what create does when the allocator returns &null. |
oom | Runs the OOM handler for a failed allocation of size bytes at align; never returns. |
ScopeFrame | One active scope: the allocator's static functions as pointers, the range snapshot taken when it opened, and the bounding box of it and every scope below it. |
ScopeChunk | SCOPE_CHUNK frames, plus their ranges packed for the scan: lo[i + 1] and span[i + 1] = hi - lo for frames[i], so a hit is one subtract and one unsigned compare. |
alloc_raw | Allocates size bytes from the allocator in effect: the innermost open AllocatorScope, else Global. |
free_raw | Gives a block back to whichever allocator it came from: the open scope whose range holds it, else Global. |
Uninitialized | Storage for one T that holds no T. |
uninitialized_from | Runs *p's destructor and returns its storage as an Uninitialized<T>. |
UninitializedSlice | Storage for cap Ts, of which [0, len) are live. |
size | Returns the number of live elements. |
capacity | Returns the number of elements the storage has room for. |
as_bytes | Returns the storage as raw bytes. |
push | Constructs a T from args in the next free slot. |
finish | Every slot is live: ownership of the cap Ts moves to the caller, who destroys them and frees capacity() * sizeof T bytes. |
clear | Destroys the live prefix, last first. |
Allocator
interface Allocator
Everything a type must have to be used as an allocator: alloc and free, both static.
Failure is &null; an allocator never aborts or panics (the OOM policy belongs to the front
end). See base/alloc.k for the full contract.
Allocator.alloc
static fn alloc(size: usize, align: usize) -> unsafe *u8
Returns size bytes aligned to align, or &null on failure.
Parameters
size: Nonzero; the front end never forwards a zero-size request.align: A power of two.
Allocator.free
static fn free(ptr: unsafe *u8, size: usize, align: usize)
Gives a block back.
Safety
ptr must be a non-null block this allocator returned, and size and align must
be
the values it was allocated (or last successfully resized) with.
Resizer
interface Resizer derives Allocator
An allocator that can grow or shrink a block without moving it.
Resizer.resize
static fn resize(ptr: unsafe *u8, old_size: usize, new_size: usize, align: usize) -> bool
Resizes the block in place. Returns false, with the block untouched, when it cannot.
Reallocator
interface Reallocator derives Allocator
An allocator that can grow or shrink a block, moving it if it has to.
Reallocator.realloc
static fn realloc(ptr: unsafe *u8, old_size: usize, new_size: usize, align: usize) -> unsafe *u8
Resizes the block, moving it if needed, and returns the new pointer. Returns &null, with
the
original block untouched, on failure.
ZeroAllocator
interface ZeroAllocator derives Allocator
An allocator that gets zeroed memory for free (fresh pages, calloc).
ZeroAllocator.alloc_zeroed
static fn alloc_zeroed(size: usize, align: usize) -> unsafe *u8
Like alloc, but every returned byte is zero. Returns &null on failure.
ScopedAllocator
interface ScopedAllocator derives Allocator
An allocator usable with AllocatorScope.
enter() returns a token that leave() takes back, and range() is the address range every
block it hands out lies in while the scope is open. No two open scopes may have overlapping
ranges.
ScopedAllocator.enter
static fn enter() -> usize
Called when a scope opens; returns a token that is passed back to leave.
ScopedAllocator.leave
static fn leave(mark: usize)
Called when the scope closes, with the token enter returned.
ScopedAllocator.range
static fn range() -> (unsafe *u8, unsafe *u8)
Returns the half-open address range [lo, hi) that all of this allocator's blocks lie in.
FullAllocator
interface FullAllocator derives Resizer + Reallocator + ZeroAllocator
An allocator with every capability: Allocator plus resize, realloc and zeroed allocation.
What the front end and @set_global_allocator take until eval if can test a capability per
instantiation; then the front end will take Allocator and fall back where one is missing.
ScopedFullAllocator
interface ScopedFullAllocator derives ScopedAllocator + FullAllocator
A FullAllocator that is also a ScopedAllocator: what AllocatorScope takes.
is_pow2
inline fn is_pow2(v: usize) -> bool
Returns true when v is a power of two (zero is not).
align_up
inline fn align_up(v: usize, align: usize) -> usize
Rounds v up to the next multiple of align.
Parameters
align: A power of two.
dangling
inline fn dangling(align: usize) -> unsafe *u8
Returns a non-null, aligned pointer that owns nothing: what a zero-size request gets.
Safety
Never dereference or free the result.
realloc_by_copy
fn <A impl Allocator> realloc_by_copy(
ptr: unsafe *u8,
old_size: usize,
new_size: usize,
align: usize
) -> unsafe *u8
A realloc for an allocator with no faster path: allocates, copies, frees the old block.
Returns &null, with the original block untouched, when the new allocation fails.
Safety
ptr must be a block A allocated with old_size and align.
set_oom_handler
fn set_oom_handler(h: fn(usize, usize) -> !)
Replaces what create does when the allocator returns &null.
The handler never returns. The default writes kairo: out of memory to stderr and exits with
code 134. The handler is process-wide, not per thread, and the call is not synchronized.
oom
fn oom(size: usize, align: usize) -> !
Runs the OOM handler for a failed allocation of size bytes at align; never returns.
ScopeFrame
struct ScopeFrame
One active scope: the allocator's static functions as pointers, the range snapshot taken when it opened, and the bounding box of it and every scope below it. 64 bytes on a 64-bit target: one cache line.
ScopeChunk
struct ScopeChunk
SCOPE_CHUNK frames, plus their ranges packed for the scan: lo[i + 1] and span[i + 1] = hi - lo for frames[i], so a hit is one subtract and one unsigned compare. Slot 0 is a sentinel (lo 0, span MAX) that every address hits, which ends the scan of a chunk without a bounds check.
alloc_raw
inline fn alloc_raw(size: usize, align: usize) -> unsafe *u8
Allocates size bytes from the allocator in effect: the innermost open AllocatorScope, else
Global. Returns &null on failure.
A zero size returns dangling(align). Pair the block with free_raw.
Parameters
align: A power of two (asserted).
Safety
The result is uninitialized memory; free it with free_raw using the same size and
align.
free_raw
inline fn free_raw(ptr: unsafe *u8, size: usize, align: usize)
Gives a block back to whichever allocator it came from: the open scope whose range holds it,
else Global.
With no scope open this is one thread-local load, one test and a tail call to Global::free.
A zero size is a no-op.
Safety
ptr must come from alloc_raw / resize_raw (or dangling for size 0), with the
size and align it was last allocated with.
Uninitialized
class <T> Uninitialized
Storage for one T that holds no T. Move-only.
Turn it into an object with construct, or give it back with release_in. This is the typed
layer between an allocator's bytes and a live object.
Uninitialized.Uninitialized
inline fn Uninitialized(self, ptr: unsafe *u8)
Wraps raw bytes. The caller vouches for size and alignment.
Uninitialized.construct
inline fn <...A> construct(self, ...args: A) -> *T
Builds a T in the storage. The only safe way to a *T.
Uninitialized.assume_init
inline fn assume_init(
self
) -> *T requires __builtin_is_trivially_copyable<T>() && __builtin_is_trivially_destructible<T>()
For a T whose bytes have all been written through as_bytes. Only for types that come into being when their bytes are written (trivially copyable and destructible).
Uninitialized.as_bytes
inline fn as_bytes(const self) -> unsafe *u8
Returns the storage as raw bytes.
Uninitialized.retype
fn <U> retype(self) -> Uninitialized<U> requires sizeof U <= sizeof T && alignof U <= alignof T
Reinterprets the storage for a U of the same or smaller size and alignment. Nothing lives in it either way.
uninitialized_from
inline fn <T> uninitialized_from(p: *T) -> Uninitialized<T>
Runs *p's destructor and returns its storage as an Uninitialized<T>.
Safety
p must point at a live T that is not used afterwards.
UninitializedSlice
class <T> UninitializedSlice
Storage for cap Ts, of which [0, len) are live.
Dropping it destroys only the live prefix; the storage itself goes back through destroy_slice
or destroy_slice_in. Move-only: a moved-from slice is left empty so its drop destroys nothing.
UninitializedSlice.UninitializedSlice
inline fn UninitializedSlice(self, ptr: unsafe *u8, cap: usize)
Wraps cap Ts' worth of raw bytes, with none of them live.
Safety
ptr must be valid for cap * sizeof T bytes at alignof T.
size
inline fn size(const self) -> usize
Returns the number of live elements.
capacity
inline fn capacity(const self) -> usize
Returns the number of elements the storage has room for.
as_bytes
inline fn as_bytes(const self) -> unsafe *u8
Returns the storage as raw bytes.
push
inline fn <...A> push(self, ...args: A)
Constructs a T from args in the next free slot.
Panics
Traps (__builtin_trap) when the slice is full.
finish
inline fn finish(self) -> *T
Every slot is live: ownership of the cap Ts moves to the caller, who destroys them and frees capacity() * sizeof T bytes.
clear
fn clear(self)
Destroys the live prefix, last first. Idempotent.
Generated from Kairo source, Apache-2.0 WITH AdditionRef-Kairo-Runtime-exception.