Backends in libualloc

A backend is a low level allocator that can request blocks or pages "from the system" on behalf of the high level allocators. Application code then uses a high level allocator, one that is the most appropriate/optimal for the expected allocation pattern.

A backend is implemented as a pair of an alloc() and a free() function and may use any field of the uall_sysalloc_t passed. The backend implementation is free to specify which fields shall be filled in by the caller.

The glue between high level allocators and backends is the uall_sysalloc_t struct, which is always filled in by the caller. Setting up an uall_sysalloc_t instance can be regarded as instantiation of a backend. The same backend instance then can be used by multiple high level allocators.

Standard backends provided with libualloc

Custom backends

The caller is free to implement new backends.

A typical use case, not portable, is requesting memory pages directly from the system, bypassing libc heap. This can be the most efficient for large number or size of allocations. Or this may allow the program to back up the high level allocator with mmap'd memory.

Hierarchical allocation: it is possible to chain allocators. For example on an embedded system there may be a backend using one large static memory block (the sram the system can use for dynamic allocations). A slab allocator is created using the uall_static_* backend; this slab allocator can split up the single memory block into "pages". Then a glue uall_sysalloc_t is built by the user that exposes the slab allocator for the next level of allocators:

 static memory block of 12k, with uall_static_* backend
 |
 +- slab of 2k slab size, wrapped in a custom uall_sysalloc_t
    |
    +- slab for struct foo, slab size 16
    |
    +- slab for struct bar, slab size 24
    |
    +- stacks for a parser AST, elem size 64