jv-pool is a small ANSI C memory-pool library that wraps repeated malloc/free
operations behind a reusable pool allocator.
It is suitable for workloads that:
- allocate many small or medium-lived objects from the same logical context
- want one-shot cleanup through
jv_pool_reset()orjv_pool_destroy() - want allocation metadata such as
existandsizeof
- single-header/single-source style API
- aligned allocations based on
max_align_t - automatic block growth when the current pool cannot satisfy a request
- dedicated large-block allocation for requests larger than the default block
alloc,alloc_nz,realloc,free,recycle,exist,sizeof,reset,dump- frees fully-idle extra blocks on
jv_pool_free()
jv_pool.h: public API and data structuresjv_pool.c: allocator implementationjv_pool_main.c: minimal usage examplejv_pool_test.c: regression tests
makeTargets produced by the Makefile:
jv_pool_mainjv_pool_test
./jv_pool_main
./jv_pool_test#include <assert.h>
#include <stdio.h>
#include <jv_pool.h>
int main(void) {
jv_pool_t *pool;
char *buf;
pool = jv_pool_create(JV_POOL_DEFAULT_SIZE, JV_POOL_SAFE_MODE);
assert(pool != NULL);
buf = jv_pool_alloc(pool, 32);
assert(buf != NULL);
jv_memset(buf, 'a', 32);
buf[31] = '\0';
printf("buf=%s size=%zu\n", buf, jv_pool_sizeof(pool, buf));
buf = jv_pool_realloc(pool, buf, 64);
assert(buf != NULL);
assert(jv_pool_sizeof(pool, buf) == 64);
jv_pool_dump(pool, stdout);
assert(jv_pool_free(pool, buf) == JV_OK);
jv_pool_destroy(pool);
return 0;
}Each pool starts with one block. A block contains one or more lumps:
- a
blockis one contiguous memory region owned by the pool - a
lumpis one allocatable fragment inside a block - small/normal allocations are served from free lumps inside existing blocks
- if no existing lump fits, the pool allocates a new block
- very large allocations may get their own dedicated block
When freeing memory:
- adjacent free lumps are merged
- if an extra block becomes entirely free, that extra block is released
- the first block stays with the pool until
resetordestroy
The API exposes two mode constants:
JV_POOL_SAFE_MODEJV_POOL_QUICK_MODE
Use JV_POOL_SAFE_MODE unless you have a compatibility reason not to.
In the current implementation both mode values are accepted, but runtime
validation behavior is effectively the same.
- All returned pointers are aligned to
max_align_t. - Requested size
0returnsNULL. - If
size < JV_POOL_MIN_SIZE,jv_pool_create()promotes it toJV_POOL_DEFAULT_SIZE. - If
size > JV_POOL_MAX_SIZE, creation/allocation fails. jv_pool_exist()andjv_pool_sizeof()only report live allocations. A pointer that has already been freed or recycled is treated as invalid.jv_pool_realloc()preserves old contents up tomin(old_size, new_size).jv_pool_alloc_nz()skips zero-fill and is the faster allocation path when the caller is going to overwrite the buffer anyway.- If
jv_pool_realloc()fails, the original pointer remains valid. - After a successful
jv_pool_realloc(), the old pointer is invalid. jv_pool_recycle()only marks a lump unused. It does not merge neighbors and does not release now-idle extra blocks.jv_pool_free()is the normal API for releasing memory. Prefer it overjv_pool_recycle()unless you explicitly want the lighter-weight behavior.- The allocator is not thread-safe.
- Any pointer obtained from the pool becomes invalid after
jv_pool_reset()orjv_pool_destroy(). - Diagnostic logging is compiled out by default. Define
JV_POOL_ENABLE_LOG=1at compile time if you want the internal trace messages.
Creates a new memory pool.
Parameters:
size: preferred block size for the poolmode:JV_POOL_SAFE_MODEorJV_POOL_QUICK_MODE
Behavior:
- if
size < JV_POOL_MIN_SIZE, the pool usesJV_POOL_DEFAULT_SIZE - if
size > JV_POOL_MAX_SIZE, creation fails - the internal block size is aligned automatically
Returns:
- pool pointer on success
NULLon failure
Allocates size bytes from the pool and zero-fills the returned memory.
Parameters:
pool: target memory poolsize: requested size in bytes
Returns:
- allocation pointer on success
NULLifpool == NULL,size == 0, size is too large, or allocation fails
Notes:
- returned memory is aligned
- actual stored size may be rounded up to the allocator alignment
Changes the size of an existing pool allocation.
Behavior:
- if
ptr == NULL, this behaves likejv_pool_alloc(pool, size) - if
size == 0, this freesptrand returnsNULL - if reallocation succeeds, old contents are copied to the new region
- if reallocation fails, the original allocation remains valid
Returns:
- new allocation pointer on success
NULLon failure
Allocates size bytes from the pool without clearing the returned memory.
Returns:
- allocation pointer on success
NULLon failure
Use this when:
- the caller will immediately overwrite the full buffer
- you want to avoid the zero-fill cost of
jv_pool_alloc()
Frees a live pool allocation.
Behavior:
- validates that
ptrbelongs to the pool and is currently allocated - merges adjacent free lumps
- may release an extra block if that block becomes entirely idle
Returns:
JV_OKon successJV_ERRORifpool/ptris invalid or the pointer is already free
Returns the stored size of a live allocation.
Returns:
- allocation size in bytes
0ifptris invalid or no longer live
Notes:
- returned size is the aligned size managed by the pool
Checks whether ptr is a live allocation from pool.
Returns:
JV_OKif the pointer is currently allocatedJV_ERRORotherwise
Marks a live allocation as unused without performing full free coalescing.
Returns:
JV_OKon successJV_ERRORif the pointer is invalid or already not live
Use carefully:
recycleis lighter weight but leaves more fragmentation behind thanfree- a recycled pointer is no longer considered valid by
exist/sizeof
Resets the pool back to its initial state.
Behavior:
- frees all extra blocks
- restores the first block to one large free lump
- invalidates all pointers previously returned by the pool
Returns:
JV_OKon successJV_ERRORifpool == NULL
Destroys the pool and frees all owned blocks.
Behavior:
- safe to call with
NULL - invalidates all outstanding pool pointers
Prints the current pool layout to fd.
Typical output includes:
- block count
- lump count
- each lump address, size, and
usedstate - each block address and size
If pool == NULL, the function returns immediately.
Iterates over all lumps in the pool.
Usage:
jv_lump_t *lump;
jv_uint_t i;
jv_pool_each_lump(pool, lump, i) {
printf("lump=%p size=%u used=%u\n", (void *) lump, lump->size, lump->used);
}Notes:
- iteration follows the pool's internal linked-list order
- do not modify pool topology while iterating
Iterates over all blocks owned by the pool.
Usage:
jv_block_t *block;
jv_uint_t i;
jv_pool_each_block(pool, block, i) {
printf("block=%p size=%zu\n", (void *) block, block->size);
}- Use
JV_POOL_SAFE_MODEas the default mode. - Prefer
jv_pool_free()overjv_pool_recycle(). - Prefer
jv_pool_alloc_nz()overjv_pool_alloc()when zero-fill is not required. - Use
jv_pool_reset()when a whole request/context ends and all allocations can be discarded together. - Do not pass pool pointers to the system
free(). - Do not use pool pointers after
free,recycle,reset,destroy, or successfulrealloc.
- not thread-safe
- optional diagnostic messages use
printfwhenJV_POOL_ENABLE_LOG=1 - pool metadata fields use bit-fields, so the allocator is not intended for
allocations larger than
JV_POOL_MAX_SIZE
This project is licensed under the MIT License. See LICENSE.