Small bump allocator written in 212 lines of C11. Used in my c02 compiler backend.
Allocations are bump-pointer within a chunk, new chunks are linked in as needed, and everything is freed at once. It suits data that is allocated together and dies together: ASTs, symbol tables, per-request scratch.
#include <stdio.h>
#include "arena.h"
typedef struct node { int value; struct node *next; } node_t;
int main(void) {
arena_t arena;
if (!arena_init(&arena, 4096)) return 1;
node_t *head = NULL;
for (int i = 0; i < 1000; i++) {
node_t *n = ARENA_ALLOC(&arena, node_t); // zeroed, no per-node free
n->value = i;
n->next = head;
head = n;
}
arena_free(&arena); // releases all 1000 nodes
return 0;
}int arena_init(arena_t *a, size_t chunk_size);Create an arena with a first chunk of chunk_size bytes (rounded up to the alignment). Returns 1 on success, 0 if malloc failed or the size is too large to represent. On failure the arena is left empty and safe to pass to arena_free.
void *arena_alloc(arena_t *a, size_t size);Allocate size zeroed bytes. Grows the arena with a new chunk when the current one is full. A request larger than the chunk size gets its own dedicated chunk, so chunk size is a tuning knob, not a limit. arena_alloc(a, 0) returns a non-NULL pointer that must not be dereferenced.
#define ARENA_ALLOC(a, TYPE)Allocate and zero sizeof(TYPE), returned as TYPE *. Use arena_alloc directly for dynamic sizes.
void arena_free(arena_t *a);Free every chunk. All pointers from the arena become invalid. Safe to call on an already-freed arena.
typedef void (*arena_oom_fn)(size_t requested);
void arena_set_oom_handler(arena_t *a, arena_oom_fn fn);Install an out-of-memory handler (NULL restores the default). Call it after arena_init, which resets it. See Out of memory.
- Every returned pointer is aligned for any standard type (
alignof(max_align_t), 16 bytes on common 64-bit targets). - Every allocation is zero-filled.
- Pointers stay valid until
arena_free; chunks are never moved or reallocated.
If malloc fails, or a request is too large to represent, arena_alloc calls the arena's handler if one is set. If the handler returns, arena_alloc returns NULL. With no handler set, the process exits with status 1, so callers who don't care can skip NULL checks.
- Not thread-safe. Use one arena per thread, or lock around it.
- Chunk tails are abandoned. When an allocation doesn't fit, the arena moves on to a new chunk and the leftover space in the old one goes unused. Pick a chunk size comfortably larger than your typical allocation.
- Small allocations take 16 bytes. Sizes round up to the alignment, which is the cost of the alignment guarantee.