#Memory Allocator
#define FIO_MEMORY_NAME fio
#include "fio-stl.h"A concurrent, per-arena memory allocator that groups objects with similar lifetimes. It tries to keep related allocations in the same chunk so cleanup is cheap and locality is decent. Define FIO_MEMORY_DISABLE to route everything to the system malloc/free instead.
The prefix fio comes from FIO_MEMORY_NAME. All public function names are built with FIO_NAME(FIO_MEMORY_NAME, ...), so a custom prefix produces my_malloc, my_free, and so on.
The module also sets the temporary allocator macros (FIO_MEM_REALLOC_, FIO_MEM_FREE_, FIO_MEM_REALLOC_IS_SAFE_, FIO_MEM_ALIGNMENT_SIZE_) for any facil.io type defined in the same include scope.
Note: FIO_MALLOC is a shortcut that defines this allocator with prefix fio and general-purpose tuning.
#Core API
#fio_malloc
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME, malloc)(size_t size);Allocates size bytes aligned to FIO_MEMORY_ALIGN_SIZE. Memory is zeroed if FIO_MEMORY_INITIALIZE_ALLOCATIONS is enabled. Allocations above the arena limit use big-block mode or mmap.
Parameters:
size— bytes to allocate.
Returns: pointer to allocated memory, or NULL on failure.
#fio_calloc
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
calloc)(size_t size_per_unit,
size_t unit_count);Equivalent to fio_malloc(size_per_unit * unit_count).
#fio_realloc
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME, realloc)(void *ptr,
size_t new_size);Reallocates ptr to new_size. Copy-free expansion is only attempted for large allocations. Prefer fio_realloc2 for predictable zeroing.
Parameters:
ptr— existing allocation, orNULL.new_size— requested new size.
Returns: pointer to the new block, or NULL on failure.
#fio_realloc2
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME, realloc2)(void *ptr,
size_t new_size,
size_t copy_len);Reallocates ptr to new_size, copying at most copy_len bytes. Memory beyond copy_len is zeroed when FIO_MEMORY_INITIALIZE_ALLOCATIONS is enabled.
Parameters:
ptr— existing allocation, orNULL.new_size— requested new size.copy_len— maximum bytes to copy from the old block.
Returns: pointer to the new block, or NULL on failure.
#fio_free
SFUNC void FIO_NAME(FIO_MEMORY_NAME, free)(void *ptr);Frees memory allocated by this allocator. Freeing a pointer from a different allocator, or after cleanup, is undefined behavior.
#fio_mmap
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME, mmap)(size_t size);Allocates directly from the system with mmap semantics. Use this for large, long-lived objects. The allocation is slower but avoids arena bookkeeping.
Parameters:
size— bytes to allocate.
Returns: pointer to allocated memory, or NULL on failure.
#fio_malloc_after_fork
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_after_fork)(void);Reinitializes allocator locks after fork in the child process. Best effort only: forking a multi-threaded process is unsafe. Prefer calling fio_state_callback_force(FIO_CALL_IN_CHILD); to reset all registered allocators.
#fio_realloc_is_safe
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, realloc_is_safe)(void);Returns non-zero if the allocator zeroes allocations, which makes fio_realloc2 safe for the uncopied tail.
#Aligned Allocation API
The aligned API requests a minimum pointer alignment. fio_malloc_aligned and fio_calloc_aligned are thin wrappers around fio_realloc_aligned, which is the single entry point.
#fio_realloc_aligned
SFUNC void *FIO_MEM_ALIGN FIO_NAME(FIO_MEMORY_NAME,
realloc_aligned)(void *ptr,
size_t new_size,
size_t copy_len,
size_t alignment);Reallocates ptr to new_size (copying at most copy_len bytes, like fio_realloc2), guaranteeing the returned pointer is aligned to at least alignment.
The alignment argument is normalized:
0selects the allocator default (FIO_MEMORY_ALIGN_SIZE).- Non power-of-2 values are rounded down to the nearest power of 2.
- The effective alignment is the maximum of the (rounded) request, the current alignment of
ptr, andFIO_MEMORY_ALIGN_SIZE— alignment never regresses across a realloc. - Values above
FIO_MEMORY_SYS_ALLOCATION_SIZEfail: returnsNULLand setserrnotoEINVAL(debug builds also tripFIO_ASSERT_DEBUG).
In-place growth only occurs when the existing pointer already satisfies the requested alignment; otherwise the data is copied to a newly aligned block.
realloc_aligned(NULL, size, 0, alignment) allocates (like malloc_aligned), and realloc_aligned(ptr, 0, copy_len, alignment) frees ptr.
#fio_malloc_aligned
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
malloc_aligned)(size_t size,
size_t alignment);Equivalent to fio_realloc_aligned(NULL, size, 0, alignment). A zero size returns the shared zero-allocation pointer (aligned only to FIO_MEMORY_ALIGN_SIZE), which may be freed or realloc'd normally.
#fio_calloc_aligned
SFUNC void *FIO_MEM_ALIGN_NEW FIO_NAME(FIO_MEMORY_NAME,
calloc_aligned)(size_t size_per_unit,
size_t unit_count,
size_t alignment);Same as fio_malloc_aligned(size_per_unit * unit_count, alignment), but zeroes the (rounded) allocation. Returns NULL on size overflow.
#fio_alloc_size
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, alloc_size)(size_t minimum_bytes);Returns the number of usable bytes the allocator actually reserves for a request of minimum_bytes (the size class): rounded up to FIO_MEMORY_ALIGN_SIZE for arena/big-block allocations, and to whole pages (minus the header offset) for page-backed ones. Alignment padding is reserved in addition to the returned value. With FIO_MEMORY_DISABLE the system allocator exposes no rounding guarantee and minimum_bytes is returned unchanged.
#Alignment abstraction macros
FIO_MEM_REALLOC_ALIGNED(ptr, old_size, new_size, copy_len, alignment)
FIO_MEM_FREE_ALIGNED(ptr, size)
FIO_MEM_ALLOC_SIZE(size)FIO_MEM_REALLOC_ALIGNED mirrors FIO_MEM_REALLOC with an added alignment argument and assigns the result to ptr (test with FIO_ASSERT_ALLOC(ptr) after the call, restoring a saved copy on failure). FIO_MEM_FREE_ALIGNED releases memory obtained through the aligned API, and FIO_MEM_ALLOC_SIZE mirrors fio_alloc_size.
The macros route to the active allocator, exactly like FIO_MEM_REALLOC does: the fio allocator when it was included (H___FIO_MALLOC___H), or portable system-allocator fallbacks otherwise. Template-local variants (FIO_MEM_REALLOC_ALIGNED_, FIO_MEM_FREE_ALIGNED_) exist for FIO_MEMORY_NAME template scope, mirroring FIO_MEM_REALLOC_ / FIO_MEM_FREE_.
Pairing rule: with the fio allocator, free accepts any pointer returned by the aligned API (interior pointers included), so FIO_MEM_FREE_ALIGNED is identical to FIO_MEM_FREE. Under FIO_MEMORY_DISABLE on Windows the aligned API uses _aligned_malloc / _aligned_realloc, whose memory must be released with FIO_MEM_FREE_ALIGNED (_aligned_free) — plain free is heap corruption. On POSIX both map to free. Custom allocators that stash alignment bookkeeping near the returned pointer should hook the macro pair, not the fio functions.
#Alignment guarantees per configuration
| Configuration | Default alignment | Aligned API backend | Max alignment |
|---|---|---|---|
| fio allocator (any OS) | FIO_MEMORY_ALIGN_SIZE (64) |
interior-pointer slice / big-block / page-backed | FIO_MEMORY_SYS_ALLOCATION_SIZE |
FIO_MEMORY_DISABLE, POSIX |
_Alignof(max_align_t) |
posix_memalign + free |
FIO_MEMORY_SYS_ALLOCATION_SIZE |
FIO_MEMORY_DISABLE, Windows |
_Alignof(max_align_t) (16) |
_aligned_malloc / _aligned_realloc / _aligned_free |
FIO_MEMORY_SYS_ALLOCATION_SIZE |
FIO_MEMORY_DISABLE, other |
_Alignof(max_align_t) |
over-allocation with stashed raw pointer | FIO_MEMORY_SYS_ALLOCATION_SIZE |
When FIO_MALLOC_OVERRIDE_SYSTEM is defined, the libc aligned-allocation family is overridden as well (aligned_alloc, posix_memalign, and on Windows _aligned_malloc / _aligned_realloc / _aligned_free), so libc-aligned pointers never reach the fio allocator (and vice versa).
#Configuration Macros
#FIO_MEMORY_NAME
#define FIO_MEMORY_NAME fioRequired prefix for the allocator's public symbols. Must be defined before including the header.
#FIO_MALLOC
#define FIO_MALLOCShortcut that defines the allocator with prefix fio and tunes it for general use. It also redefines FIO_MEM_REALLOC, FIO_MEM_FREE, FIO_MEM_REALLOC_IS_SAFE, and FIO_MEM_ALIGNMENT_SIZE.
#FIO_MEMORY_DISABLE
#define FIO_MEMORY_DISABLEBypasses the custom allocator and routes all allocations to the system malloc/free.
#FIO_MEMORY_ALIGN_LOG
#define FIO_MEMORY_ALIGN_LOG 6Log2 of the allocation alignment. Clamped to the range 3–10 (8 to 1024 bytes). Default is 6 (64 bytes).
#FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG
#define FIO_MEMORY_SYS_ALLOCATION_SIZE_LOG 21Log2 of the chunk size allocated from the system. Clamped to 17–24. Default is 21 (~2 MB).
#FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG
#define FIO_MEMORY_BLOCKS_PER_ALLOCATION_LOG 2Log2 of the number of blocks per system chunk. Range 0–4. Affects fragmentation and the threshold for big allocations.
#FIO_MEMORY_ENABLE_BIG_ALLOC
#define FIO_MEMORY_ENABLE_BIG_ALLOC 1When enabled, large allocations can consume an entire chunk as a single big block. May increase fragmentation for long-lived objects.
#FIO_MEMORY_ARENA_COUNT
#define FIO_MEMORY_ARENA_COUNT -1Number of arenas. Negative or zero means dynamic selection based on CPU core count, capped by FIO_MEMORY_ARENA_COUNT_MAX and fallback FIO_MEMORY_ARENA_COUNT_FALLBACK.
#FIO_MEMORY_ARENA_COUNT_FALLBACK
#define FIO_MEMORY_ARENA_COUNT_FALLBACK 24Default arena count when dynamic CPU detection fails.
#FIO_MEMORY_ARENA_COUNT_MAX
#define FIO_MEMORY_ARENA_COUNT_MAX 64Upper bound for dynamic arena count.
#FIO_MEMORY_USE_THREAD_MUTEX
#define FIO_MEMORY_USE_THREAD_MUTEX 0When 1, arenas protect themselves with pthread mutexes instead of spinlocks. Default is 1 when FIO_MEMORY_ARENA_COUNT is positive.
#FIO_MEMORY_INITIALIZE_ALLOCATIONS
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS \
FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULTWhen 1, allocations return zeroed memory and realloc2 zeroes uncopied bytes. Default is 1.
#FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT
#define FIO_MEMORY_INITIALIZE_ALLOCATIONS_DEFAULT 1Default value for FIO_MEMORY_INITIALIZE_ALLOCATIONS.
#FIO_MEMORY_CACHE_SLOTS
#define FIO_MEMORY_CACHE_SLOTS 4Number of freed chunks to keep cached before returning memory to the system.
#FIO_MEMORY_WARMUP
#define FIO_MEMORY_WARMUP 0If set to a positive number, that many arenas pre-allocate a block at startup. Usually best left at 0.
#FIO_MEM_SYS_ALLOC, FIO_MEM_SYS_REALLOC, FIO_MEM_SYS_FREE
#define FIO_MEM_SYS_ALLOC(pages, alignment_log) /* ... */
#define FIO_MEM_SYS_REALLOC(ptr, old_pages, new_pages, alignment_log) /* ... */
#define FIO_MEM_SYS_FREE(ptr, pages) /* ... */Override all three to replace the system allocation backend. Alignment is essential and may be large.
#Introspection
These functions are exposed for debugging. Treat them as unstable.
#fio_malloc_arenas
SFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_arenas)(void);Returns the number of arenas.
#fio_malloc_block_size
SFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_block_size)(void);Returns the block size used for arena allocations.
#fio_malloc_sys_alloc_size
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_sys_alloc_size)(void);Returns the system chunk size in bytes.
#fio_malloc_cache_slots
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_cache_slots)(void);Returns the configured number of cache slots.
#fio_malloc_alignment
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alignment)(void);Returns the allocation alignment in bytes.
#fio_malloc_alignment_log
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alignment_log)(void);Returns log2 of the allocation alignment.
#fio_malloc_alloc_limit
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_alloc_limit)(void);Returns the size above which allocations bypass arena blocks.
#fio_malloc_arena_alloc_limit
FIO_IFUNC size_t FIO_NAME(FIO_MEMORY_NAME, malloc_arena_alloc_limit)(void);Returns the arena block size limit.
#fio_malloc_print_state
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_state)(void);Prints allocator state to stderr.
#fio_malloc_print_free_block_list
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_free_block_list)(void);Prints the free-block list to stderr.
#fio_malloc_print_settings
SFUNC void FIO_NAME(FIO_MEMORY_NAME, malloc_print_settings)(void);Prints allocator settings to stderr.
#Example
#define FIO_MALLOC
#include "fio-stl.h"
int main(void) {
char *buf = fio_malloc(256);
if (!buf) return -1;
fio_memcpy(buf, "hello", 5);
fprintf(stderr, "%s\n", buf);
char *buf2 = fio_realloc2(buf, 512, 5);
if (!buf2) {
fio_free(buf);
return -1;
}
buf = buf2;
fio_free(buf);
return 0;
}