Arena and pool allocators that operate entirely within caller-provided backing stores. No heap allocation, no OS syscalls -- suitable for embedded systems, real-time code, and any context where deterministic memory management is required.
Bump allocator with mark/reset semantics and optional memory poisoning for debugging.
| Type | Description |
|---|---|
Arena |
Allocator state record with base, size, pos, highwater, failed, poison, and overflow fields. |
OverflowProc |
PROCEDURE(ADDRESS, CARDINAL) -- callback invoked when Alloc would fail. Receives the arena address and the requested size. May grow the backing store, log, or halt. |
Lifecycle
PROCEDURE Init(VAR a: Arena; base: ADDRESS; size: CARDINAL);
Initialize an arena over the backing store [base..base+size). Sets pos=0, highwater=0, failed=0, poison=FALSE.
Allocation
PROCEDURE Alloc(VAR a: Arena; n: CARDINAL; align: CARDINAL;
VAR p: ADDRESS; VAR ok: BOOLEAN);
Allocate n bytes with the given alignment (must be a power of two). On success: p points to the allocated region, ok=TRUE. On failure: p=NIL, ok=FALSE, failed-allocs counter incremented.
Mark / Reset
PROCEDURE Mark(VAR a: Arena): CARDINAL;
Return the current position for later ResetTo.
PROCEDURE ResetTo(VAR a: Arena; mark: CARDINAL);
Reset pos to mark. If mark > pos, this is a no-op. If poisoning is on, freed bytes are zero-filled.
PROCEDURE Clear(VAR a: Arena);
Reset pos to 0.
Queries
PROCEDURE Remaining(VAR a: Arena): CARDINAL;
Bytes remaining in the arena.
PROCEDURE HighWater(VAR a: Arena): CARDINAL;
Peak allocation position ever reached.
PROCEDURE FailedAllocs(VAR a: Arena): CARDINAL;
Number of failed Alloc calls.
Poisoning
PROCEDURE PoisonOn(VAR a: Arena);
Enable poison: Alloc fills allocated bytes with 0CDH, ResetTo fills freed bytes with 0.
PROCEDURE PoisonOff(VAR a: Arena);
Disable poison.
Overflow Handling
PROCEDURE SetOverflowHandler(VAR a: Arena; handler: OverflowProc);
Set a callback invoked when Alloc would fail. The handler may grow the backing store, log, or halt. If the handler does not resolve the overflow, Alloc returns NIL.
Fixed-size block allocator with an intrusive LIFO free list and optional poisoning.
| Type | Description |
|---|---|
Pool |
Allocator state record with base, size, blockSize, blockCount, freeHead, inUse, highwater, invalidFree, and poison fields. |
Lifecycle
PROCEDURE Init(VAR p: Pool; base: ADDRESS; size: CARDINAL;
blockSize: CARDINAL; VAR ok: BOOLEAN);
Initialize a pool over [base..base+size) with the given block size (rounded up to ADDRESS alignment). On success: ok=TRUE and the free list is built. Fails if blockSize is too small or zero blocks fit.
Allocation
PROCEDURE Alloc(VAR p: Pool; VAR out: ADDRESS; VAR ok: BOOLEAN);
Allocate one block. On success: out points to the block. On failure (pool exhausted): out=NIL, ok=FALSE.
PROCEDURE Free(VAR p: Pool; addr: ADDRESS; VAR ok: BOOLEAN);
Return a block to the pool. Validates that addr is non-NIL, within pool range, and aligned. On failure: ok=FALSE, invalid-frees counter incremented.
Queries
PROCEDURE InUse(VAR p: Pool): CARDINAL;
Number of blocks currently allocated.
PROCEDURE HighWater(VAR p: Pool): CARDINAL;
Peak number of blocks ever allocated simultaneously.
PROCEDURE InvalidFrees(VAR p: Pool): CARDINAL;
Number of invalid Free calls.
Poisoning
PROCEDURE PoisonOn(VAR p: Pool);
Enable poison: Alloc fills with 0CDH, Free fills with 0DDH.
PROCEDURE PoisonOff(VAR p: Pool);
Disable poison.
Pure utility procedures for pointer arithmetic, alignment, and byte-level memory access. No state, no side effects.
| Type | Description |
|---|---|
ByteArray |
ARRAY [0..65535] OF CHAR -- overlay for indexed byte access. |
BytePtr |
POINTER TO ByteArray -- cast an ADDRESS to access individual bytes. |
AddrPtr |
POINTER TO ADDRESS -- read/write an ADDRESS-sized value at any location. |
Alignment
PROCEDURE IsPowerOfTwo(x: CARDINAL): BOOLEAN;
Returns TRUE if x is a power of two (x > 0).
PROCEDURE AlignUp(x, align: CARDINAL): CARDINAL;
Round x up to the next multiple of align. align must be a power of two; otherwise returns x unchanged.
Pointer Arithmetic
PROCEDURE PtrAdd(base: ADDRESS; offset: CARDINAL): ADDRESS;
Return base + offset as an ADDRESS.
PROCEDURE PtrDiff(a, b: ADDRESS): CARDINAL;
Return a - b as a CARDINAL. Returns 0 if b >= a.
Byte Access
PROCEDURE FillBytes(base: ADDRESS; count: CARDINAL; val: CARDINAL);
Fill count bytes starting at base with val (0..255).
PROCEDURE ReadAddr(loc: ADDRESS): ADDRESS;
Read an ADDRESS from memory location loc.
PROCEDURE WriteAddr(loc: ADDRESS; val: ADDRESS);
Write val as an ADDRESS at memory location loc.
MODULE AllocDemo;
FROM SYSTEM IMPORT ADDRESS, ADR, SIZE;
FROM Arena IMPORT Arena, Init, Alloc, Mark, ResetTo, Clear,
Remaining, PoisonOn;
FROM Pool IMPORT Pool;
VAR
arenaBuf: ARRAY [0..4095] OF CHAR;
poolBuf: ARRAY [0..4095] OF CHAR;
a: Arena;
p: Pool;
ptr1, ptr2: ADDRESS;
mark: CARDINAL;
ok: BOOLEAN;
BEGIN
(* Arena: bump allocator with mark/reset *)
Init(a, ADR(arenaBuf), SIZE(arenaBuf));
PoisonOn(a);
mark := Mark(a);
Alloc(a, 128, 8, ptr1, ok); (* 128 bytes, 8-byte aligned *)
Alloc(a, 256, 16, ptr2, ok); (* 256 bytes, 16-byte aligned *)
ResetTo(a, mark); (* free both allocations at once *)
(* Pool: fixed-size block allocator *)
Pool.Init(p, ADR(poolBuf), SIZE(poolBuf), 64, ok);
Pool.Alloc(p, ptr1, ok); (* get a 64-byte block *)
Pool.Alloc(p, ptr2, ok); (* get another *)
Pool.Free(p, ptr1, ok); (* return first block *)
Clear(a);
END AllocDemo.