zalloc.h is a single-header, high-performance memory management suite for C/C++.
It unifies three specialized allocators, Arenas (Linear), Pools (Fixed-Block), and Debug (Leak Detection), into a cohesive API. It is designed to replace standard malloc/free in systems where performance, cache locality, and predictable lifetimes are critical.
Unlike general-purpose allocators that must handle fragmentation and thread safety at a high cost, zalloc provides specialized tools for specific patterns: linear data processing (Arenas) and node-based structures (Pools).
Z-ARENA (Linear Allocator):
- Zero-Overhead: Allocations are just a pointer increment.
- Instant Cleanup: Free millions of objects in
$O(1)$ by resetting the arena. - Cache Friendly: Data is packed sequentially in memory blocks.
- Ideal For: Per-frame data, request handling, parsing, loading levels.
Z-POOL (Fixed-Block Allocator):
- No Fragmentation: Slices memory into perfect fixed-size slots.
$O(1)$ Operations: Allocation and Free take constant time (free-list pop/push).- Ideal For: Graphs, Linked Lists, ECS Entities, and Stable Maps.
Z-DEBUG (Leak Detector):
- Drop-in Replacement: Wraps standard allocators to track file/line info.
- Buffer Guard: Detects buffer overflows (canaries) and double-frees.
- Leak Reporting: Prints a detailed report of leaked blocks on exit.
Zero Dependencies: No linking required. Just drop it in and go.
- Copy
zalloc.hto your project's include folder. - Create one C file to hold the implementation:
// zalloc.c#defineZALLOC_IMPLEMENTATION#include"zalloc.h"You can also do this directly from your source files without creating a new one.
Arenas are the fastest way to manage memory for temporary tasks.
#include"zalloc.h"voidprocess_request()
{
// Initialize.zarenascratch;
zarena_init(&scratch);
// Allocate rapidly (no free needed per item).int*data=zarena_alloc(&scratch, 100*sizeof(int));
char*str=zarena_alloc(&scratch, 64);
// Realloc (Optimized to extend in-place if possible).data=zarena_realloc(&scratch, data, 100*4, 200*4);
// Instant Cleanup (Frees all blocks at once).zarena_free(&scratch);
}Tip: Use
zarena_reset(&scratch)to keep the memory blocks allocated but mark them as empty. This effectively makes memory allocation "free" for the next frame/request.
Pools are mathematically optimal for systems where you allocate and free many items of the same size.
#include"zalloc.h"structNode
{
intid;
floatx, y;
};
voidgame_loop()
{
// Initialize pool for 'struct Node', 100 items per block.zpoolentities;
zpool_init(&entities, sizeof(structNode), 100);
// Allocate (O(1)).structNode*n1=zpool_alloc(&entities);
structNode*n2=zpool_alloc(&entities);
// Free individually (O(1)) - Returns slot to free list.zpool_recycle(&entities, n1);
// Cleanup everything.zpool_free(&entities);
}Use this module during development to catch leaks and corruption.
#include"zalloc.h"voiddebug_mode()
{
// Use macros to auto-capture File and Line.void*p=zdebug_malloc(100);
// ... forgot to free p ...// Print report.size_tleaks=zdebug_print_leaks();
// Output: // [Leak] 100 bytes at 0x... (alloc: main.c:15).
}| Function | Description |
|---|---|
zarena_init(zarena *a) | Initializes the arena structure. |
zarena_alloc(zarena *a, size_t size) | Allocates size bytes. Returns NULL on failure. |
zarena_alloc_zero(zarena *a, size_t size) | Allocates and zero-initializes memory (like calloc). |
zarena_alloc_align(zarena *a, size_t size, size_t align) | Allocates memory with specific alignment (must be power of 2). |
zarena_realloc(zarena *a, void *ptr, size_t old_sz, size_t new_sz) | Resizes memory. Optimized if ptr was the last allocation. |
zarena_reset(zarena *a) | Resets usage counters but keeps allocated blocks for reuse. Fast clear. |
zarena_free(zarena *a) | Frees all memory blocks and resets the struct. |
| Function | Description |
|---|---|
zpool_init(zpool *p, size_t item_size, size_t per_block) | Initializes pool for items of item_size. |
zpool_alloc(zpool *p) | Returns a pointer to a free slot. |
zpool_recycle(zpool *p, void *ptr) | Returns a slot to the free list. |
zpool_free(zpool *p) | Frees all underlying memory blocks. |
| Function | Description |
|---|---|
zdebug_malloc(size_t size) | Allocates memory with header tracking file/line. |
zdebug_calloc(size_t n, size_t size) | Allocates zero-initialized tracked memory. |
zdebug_realloc(void *ptr, size_t size) | Reallocates tracked memory. Checks canaries/magic. |
zdebug_free(void *ptr) | Frees memory. Validates guard bytes/magic before freeing. |
zdebug_print_leaks(void) | Prints a summary of all currently allocated blocks to stderr. |
zdebug_register_atexit(void) | Registers zdebug_print_leaks to run automatically when the program exits. |
zalloc was designed to power the z-libs ecosystem. You can override the memory backend of the other libraries to use an Arena or Pool.
This makes map_free instantaneous and puts all map buckets into contiguous cache lines.
// Define global context for the macros.staticzarena*g_map_arena=NULL;
// Override zmap memory macros.#defineZ_MAP_MALLOC(sz) zarena_alloc(g_map_arena, sz)
#defineZ_MAP_CALLOC(n, sz) zarena_alloc_zero(g_map_arena, (n)*(sz))
#defineZ_MAP_REALLOC(p, sz) zarena_realloc(g_map_arena, p, 0, sz)
#defineZ_MAP_FREE(p) /* No-op: Arena handles free. */#include"zmap.h"// ... Register types ...intmain()
{
zarenaa; zarena_init(&a);
g_map_arena=&a; // Set context.// Map uses arena memory automatically.map_IntIntm=map_init(IntInt, NULL, NULL);
map_put(&m, 1, 100);
// Frees the map buckets instantly.zarena_free(&a); }You can configure zalloc behavior by defining these macros before including the header.
| Macro | Description | Default |
|---|---|---|
ZALLOC_ARENA_ONLY | Only compile the Arena module (saves binary size). | Disabled |
ZALLOC_POOL_ONLY | Only compile the Pool module. | Disabled |
ZALLOC_DEBUG_ONLY | Only compile the Debug module. | Disabled |
Z_MALLOC, Z_FREE | Override the underlying backend (e.g., use OS calls directly). | stdlib.h |
ZARENA_DEFAULT_BLOCK_SIZE | Initial size of arena blocks in bytes. | 4096 |
ZDEBUG_LOCK | Define mutex lock for thread-safe debug logging. | No-op |
MIT License. See LICENSE for details.