Skip to content

Dynamic-Arena-Allocator is a fast, lightweight C single-header arena allocator. It pre-allocates memory and hands out slices quickly, freeing all allocations at once. Ideal for temporary buffers, batch processing, game memory, and parsers. Just include `arena.h` to get started.

Notifications You must be signed in to change notification settings

Voltamon/Dynamic-Arena-Allocator

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

3 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Arena Allocator

A fast, simple, single-header arena/region allocator for C.

What is an Arena Allocator?

An arena allocator is a memory management strategy where you allocate a large block of memory upfront, then quickly hand out pieces of it by simply incrementing a pointer. When you're done, you free everything at once.

Visual Representation

Initial State:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Arena (4096 bytes)                  β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚         Empty Memory            β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ offset: 0                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

After Allocations:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Arena (4096 bytes)                  β”‚
β”‚ β”Œβ”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ A  β”‚  B  β”‚   C    β”‚    Empty     β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ offset: 940                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

API Pattern

This arena uses a self-referential API pattern. Each arena contains a self pointer that references itself, and you call methods using:

arena->function(arena->self, parameters...)

Visual Explanation

Arena* arena = Arena_create(4096);
       ↓
   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
   β”‚ Arena struct            β”‚
   β”‚  self ──┐               β”‚
   β”‚  alloc  β”‚  ← function   β”‚
   β”‚  destroyβ”‚     pointers  β”‚
   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
             β”‚
             └─→ Points to itself

Usage Examples

int* data = arena->alloc(arena->self, sizeof(int) * 100);
arena->destroy(arena->self);
size_t mark = arena->get_mark(arena->self);
arena->reset_to_mark(arena->self, mark);

The pattern is: arena->method_name(arena->self, other_params...)

Why Use an Arena?

Traditional malloc/free (SLOW)

int* a = malloc(sizeof(int) * 10);     // System call
char* b = malloc(100);                  // System call
double* c = malloc(sizeof(double) * 5); // System call

free(a);  // Track each allocation
free(b);  // Easy to leak memory
free(c);  // Manual bookkeeping

Arena (FAST)

Arena* arena = Arena_create(4096);  // One allocation

int* a = arena->alloc(arena->self, sizeof(int) * 10);     // Pointer math
char* b = arena->alloc(arena->self, 100);                  // Pointer math
double* c = arena->alloc(arena->self, sizeof(double) * 5); // Pointer math

arena->destroy(arena->self);  // Free everything at once

Performance Comparison

Operation Arena malloc/free
Allocation Speed O(1) 2-3 cycles O(log n) 100+ cycles
Per-alloc Overhead 0 bytes 8-16 bytes
Batch Free O(1) O(n)
Fragmentation None Possible
Cache Performance Sequential Random

Installation

Copy arena.h to your project. Done.

Usage

Step 1: Include the Header

In ONE .c file:

#define ARENA_IMPLEMENTATION
#include "arena.h"

In all other files:

#include "arena.h"

Step 2: Create an Arena

Arena* arena = Arena_create(4096);

Step 3: Allocate Memory

int* numbers = (int*)arena->alloc(arena->self, sizeof(int) * 100);
char* message = (char*)arena->alloc(arena->self, 256);

Step 4: Use the Memory

for (int i = 0; i < 100; i++) {
    numbers[i] = i * i;
}
sprintf(message, "Arena allocator works!");

Step 5: Clean Up

arena->destroy(arena->self);

Complete Example

#define ARENA_IMPLEMENTATION
#include "arena.h"

int main(void) {
    Arena* arena = Arena_create(4096);

    int* data = (int*)arena->alloc(arena->self, sizeof(int) * 100);
    for (int i = 0; i < 100; i++) {
        data[i] = i * i;
    }

    printf("First: %d, Last: %d\n", data[0], data[99]);

    arena->destroy(arena->self);
    return 0;
}

Compile:

gcc your_file.c -o program
./program

API Reference

Creation and Destruction

Arena* Arena_create(size_t size);

Creates a new arena with the specified size in bytes.

arena->destroy(arena->self);

Destroys the arena and frees all memory.

Allocation Functions

void* ptr = arena->alloc(arena->self, size_t size);

Allocates size bytes from the arena. Returns pointer or NULL on failure.

Example: int* data = (int*)arena->alloc(arena->self, sizeof(int) * 100);

void* ptr = arena->alloc_aligned(arena->self, size_t size, size_t alignment);

Allocates size bytes aligned to alignment boundary (must be power of 2).

Example: float* data = (float*)arena->alloc_aligned(arena->self, 1024, 16);

void* new_ptr = arena->realloc(arena->self, void* ptr, size_t old_size, size_t new_size);

Reallocates memory. Tries to expand in place if possible.

Example: str = (char*)arena->realloc(arena->self, str, 10, 50);

Memory Management

arena->reset(arena->self);

Resets all allocations. Memory becomes available again but is not freed.

size_t mark = arena->get_mark(arena->self);

Returns current allocation offset (checkpoint).

Example: size_t checkpoint = arena->get_mark(arena->self);

arena->reset_to_mark(arena->self, size_t mark);

Restores arena to a previously saved checkpoint.

Example: arena->reset_to_mark(arena->self, checkpoint);

bool success = arena->resize(arena->self, size_t new_size);

Manually resizes the current arena chunk.

Example: if (arena->resize(arena->self, 4096)) { ... }

Diagnostics

arena->print_stats(arena->self);

Prints detailed memory usage statistics.

How It Works

Bump Allocation

The arena uses "bump allocation" - the fastest allocation strategy:

void* ptr = arena->memory + arena->offset;
arena->offset += size;
return ptr;

That's it. Just 2-3 CPU cycles.

Visual: Bump Allocation

Step 1: alloc(40 bytes)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ 40B  β”‚      Empty Memory          β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ offset: 40 ←                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step 2: alloc(100 bytes)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ 40B  β”‚ 100B   β”‚   Empty Memory    β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ offset: 140 ←                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step 3: alloc(200 bytes)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ 40B  β”‚ 100B   β”‚ 200B   β”‚  Empty  β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ offset: 340 ←                       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Automatic Growth

When the arena runs out of space, it automatically creates a new chunk:

Arena Full:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Chunk 1 (4096 bytes) - FULL         β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         ↓ Need more space
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Chunk 1 (4096 bytes) - FULL         β”‚ ──→ β”‚ Chunk 2 (8192 bytes)                β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚     β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ”‚ β”‚     β”‚ β”‚ Active β”‚      Empty           β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚     β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Checkpoint and Restore

Save your position, do temporary work, then restore. Marks work across multiple chunks automatically.

State 1: Save Checkpoint
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Used    β”‚  Empty                  β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ checkpoint = 500 ←                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

State 2: Temporary Allocations (Multi-Chunk)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Chunk 1                             β”‚ ──→ β”‚ Chunk 2                             β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚     β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Used    β”‚  Temp in Chunk 1        β”‚ β”‚     β”‚ β”‚ Temp 2   β”‚  Empty              β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚     β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

State 3: Restore to Checkpoint (Resets Both Chunks)
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Chunk 1                             β”‚ ──→ β”‚ Chunk 2                             β”‚
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚     β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚ β”‚ Used    β”‚  Available Again!       β”‚ β”‚     β”‚ β”‚  Reset to 0                     β”‚ β”‚
β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚     β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚ offset: 500 ← restored              β”‚     β”‚ offset: 0                           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Note: Marks track absolute position across all chunks, so you can save a checkpoint in one chunk and reset from another chunk safely.

Common Patterns

Pattern 1: Game Frame Allocations

Arena* frame_arena = Arena_create(1024 * 1024);

while (game_running) {
    Entity* entities = frame_arena->alloc(frame_arena->self, sizeof(Entity) * count);
    Particle* particles = frame_arena->alloc(frame_arena->self, sizeof(Particle) * p_count);

    render_frame(entities, particles);

    frame_arena->reset(frame_arena->self);
}

frame_arena->destroy(frame_arena->self);

Pattern 2: Temporary Scratch Memory

Arena* scratch = Arena_create(4096);

void process_data(void) {
    size_t checkpoint = scratch->get_mark(scratch->self);

    char* temp_buffer = scratch->alloc(scratch->self, 2048);
    int* temp_array = scratch->alloc(scratch->self, sizeof(int) * 1000);

    scratch->reset_to_mark(scratch->self, checkpoint);
}

Pattern 3: String Building

Arena* string_arena = Arena_create(4096);

char* build_path(Arena* a, const char* dir, const char* file) {
    size_t len = strlen(dir) + strlen(file) + 2;
    char* path = a->alloc(a->self, len);
    sprintf(path, "%s/%s", dir, file);
    return path;
}

char* path1 = build_path(string_arena, "/usr", "bin");
char* path2 = build_path(string_arena, "/home", "user");

string_arena->destroy(string_arena->self);

Pattern 4: Aligned SIMD Data

Arena* simd_arena = Arena_create(8192);

float* vectors = (float*)simd_arena->alloc_aligned(simd_arena->self,
                                                     sizeof(float) * 1024,
                                                     16);

simd_arena->destroy(simd_arena->self);

When to Use

Perfect For:

  • Game frame allocations
  • Parsers and compilers (AST nodes)
  • Loading data files
  • String manipulation
  • Temporary buffers
  • Batch processing

Not Ideal For:

  • Long-lived objects with different lifetimes
  • When you need to free individual items
  • Thread-shared memory without synchronization

Visual: Arena vs malloc/free

malloc/free Pattern

malloc(40)  ──→  [Block A]  System call
malloc(100) ──→  [Block B]  System call
malloc(200) ──→  [Block C]  System call

free(A)     ──→  Track each  System call
free(B)     ──→  allocation  System call
free(C)     ──→  manually    System call

Arena Pattern

Arena_create(4096)  ──→  [Large Block]  One system call

arena->alloc(arena->self, 40)   ──→  [A|B|C|Empty]  Just pointer math
arena->alloc(arena->self, 100)  ──→  [A|B|C|Empty]  Just pointer math
arena->alloc(arena->self, 200)  ──→  [A|B|C|Empty]  Just pointer math

arena->destroy(arena->self)     ──→  Free all        One call

Multi-File Projects

main.c

#define ARENA_IMPLEMENTATION
#include "arena.h"

int main(void) {
    Arena* arena = Arena_create(4096);
    process_data(arena);
    arena->destroy(arena->self);
    return 0;
}

module.c

#include "arena.h"

void process_data(Arena* arena) {
    int* data = arena->alloc(arena->self, sizeof(int) * 100);
}

module.h

#include "arena.h"
void process_data(Arena* arena);

Thread Safety

Not thread-safe by default. Use one arena per thread:

_Thread_local Arena* thread_arena = NULL;

void thread_init(void) {
    thread_arena = Arena_create(64 * 1024);
}

void thread_cleanup(void) {
    thread_arena->destroy(thread_arena->self);
}

Benchmarks

Typical performance on modern hardware:

Metric Arena malloc
Allocation 2-3 cycles 100+ cycles
1000 allocations ~0.001ms ~0.1ms
Overhead per alloc 0 bytes 8-16 bytes
Free all O(1) O(n)

Tips

  1. Pre-allocate if you know the size

    Arena* arena = Arena_create(known_total_size);
  2. Use checkpoints for nested operations

    size_t mark = arena->get_mark(arena->self);
    arena->reset_to_mark(arena->self, mark);
  3. Reset instead of destroy for repeated operations

    for (int i = 0; i < iterations; i++) {
        arena->reset(arena->self);
    }
  4. Monitor usage during development

    arena->print_stats(arena->self);
  5. Use aligned allocations for SIMD

    float* data = arena->alloc_aligned(arena->self, size, 16);

Troubleshooting

"Undefined reference to Arena_create"

One .c file must have #define ARENA_IMPLEMENTATION before including.

"Multiple definition of Arena_create"

Only ONE file should have #define ARENA_IMPLEMENTATION.

Out of memory

Increase initial size or use reset to reuse memory.

Example Program

See example.c for comprehensive examples covering all features.

Compile:

gcc example.c -o example
./example

Or:

clang example.c -o example
./example

License

Public domain. Use however you want.

About

Dynamic-Arena-Allocator is a fast, lightweight C single-header arena allocator. It pre-allocates memory and hands out slices quickly, freeing all allocations at once. Ideal for temporary buffers, batch processing, game memory, and parsers. Just include `arena.h` to get started.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages