Add pool allocator
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
// vim:fileencoding=utf-8:foldmethod=marker
|
||||
|
||||
#include "pr_pool_allocator.h"
|
||||
#include "../../vendor/wapp/os/mem/mem_os.h"
|
||||
#include <string.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Intrusive free list node — reuses the first sizeof(void*) bytes of each slot
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
typedef struct PrPoolFreeNode PrPoolFreeNode;
|
||||
struct PrPoolFreeNode {
|
||||
PrPoolFreeNode *next;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Internal: allocate a new block and carve it into the free list
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
wp_intern b8 _prPoolGrow(PrPool *pool) {
|
||||
// Grow blocks array if full
|
||||
if (pool->block_count == pool->block_cap) {
|
||||
u64 new_cap = pool->block_cap ? pool->block_cap * 2 : 4;
|
||||
u64 new_size = sizeof(void *) * new_cap;
|
||||
void **new_arr = (void **)wpOsMemAlloc(NULL, new_size, WP_MEM_ACCESS_READ_WRITE,
|
||||
WP_MEM_ALLOC_RESERVE, WP_MEM_INIT_UNINITIALISED);
|
||||
if (!new_arr) { return false; }
|
||||
|
||||
if (pool->blocks) {
|
||||
memcpy(new_arr, pool->blocks, sizeof(void *) * pool->block_count);
|
||||
wpOsMemFree(pool->blocks, sizeof(void *) * pool->block_cap);
|
||||
}
|
||||
|
||||
pool->blocks = new_arr;
|
||||
pool->block_cap = new_cap;
|
||||
}
|
||||
|
||||
// Allocate the block
|
||||
u64 block_bytes = pool->alloc_size * pool->block_slots;
|
||||
void *block = wpOsMemAlloc(NULL, block_bytes, WP_MEM_ACCESS_READ_WRITE,
|
||||
WP_MEM_ALLOC_RESERVE, WP_MEM_INIT_UNINITIALISED);
|
||||
if (!block) { return false; }
|
||||
|
||||
pool->blocks[pool->block_count++] = block;
|
||||
pool->total += pool->block_slots;
|
||||
|
||||
// Carve into free list (in reverse so the first slot ends up on top)
|
||||
u8 *bytes = (u8 *)block;
|
||||
for (u64 i = pool->block_slots; i > 0; --i) {
|
||||
PrPoolFreeNode *node = (PrPoolFreeNode *)bytes;
|
||||
node->next = (PrPoolFreeNode *)pool->free_list;
|
||||
pool->free_list = node;
|
||||
bytes += pool->alloc_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void prPoolInit(PrPool *pool, u64 slot_size, u64 initial_slots) {
|
||||
memset(pool, 0, sizeof(*pool));
|
||||
pool->slot_size = slot_size;
|
||||
pool->alloc_size = slot_size < sizeof(PrPoolFreeNode) ? sizeof(PrPoolFreeNode) : slot_size;
|
||||
|
||||
// Enforce minimum block size of 4096 bytes
|
||||
u64 min_slots = (4096 + pool->alloc_size - 1) / pool->alloc_size;
|
||||
pool->block_slots = initial_slots > min_slots ? initial_slots : min_slots;
|
||||
|
||||
_prPoolGrow(pool);
|
||||
}
|
||||
|
||||
void *prPoolAlloc(PrPool *pool) {
|
||||
// Grow if free list is empty
|
||||
if (!pool->free_list) {
|
||||
if (!_prPoolGrow(pool)) { return NULL; }
|
||||
}
|
||||
|
||||
PrPoolFreeNode *node = (PrPoolFreeNode *)pool->free_list;
|
||||
pool->free_list = node->next;
|
||||
pool->active++;
|
||||
return node;
|
||||
}
|
||||
|
||||
void prPoolFree(PrPool *pool, void *slot) {
|
||||
if (!slot) { return; }
|
||||
|
||||
PrPoolFreeNode *node = (PrPoolFreeNode *)slot;
|
||||
node->next = (PrPoolFreeNode *)pool->free_list;
|
||||
pool->free_list = node;
|
||||
pool->active--;
|
||||
}
|
||||
|
||||
void prPoolDestroy(PrPool *pool) {
|
||||
u64 block_bytes = pool->alloc_size * pool->block_slots;
|
||||
for (u64 i = 0; i < pool->block_count; ++i) {
|
||||
wpOsMemFree(pool->blocks[i], block_bytes);
|
||||
}
|
||||
if (pool->blocks) {
|
||||
wpOsMemFree(pool->blocks, sizeof(void *) * pool->block_cap);
|
||||
}
|
||||
memset(pool, 0, sizeof(*pool));
|
||||
}
|
||||
|
||||
u64 prPoolTotalSlots(const PrPool *pool) {
|
||||
return pool->total;
|
||||
}
|
||||
|
||||
u64 prPoolActiveSlots(const PrPool *pool) {
|
||||
return pool->active;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// vim:fileencoding=utf-8:foldmethod=marker
|
||||
|
||||
#ifndef PR_POOL_ALLOCATOR_H
|
||||
#define PR_POOL_ALLOCATOR_H
|
||||
|
||||
#include "../../vendor/wapp/common/aliases/aliases.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Pool allocator — fixed-size slot allocator with self-managed growth
|
||||
//
|
||||
// Manages fixed-size slots arranged in contiguous blocks. The pool owns its
|
||||
// memory (via wapp OS allocation) and grows on demand when the free list is empty.
|
||||
//
|
||||
// Usage:
|
||||
// PrPool pool;
|
||||
// prPoolInit(&pool, sizeof(Edge), 64);
|
||||
// Edge *e = prPoolAlloc(&pool);
|
||||
// prPoolFree(&pool, e);
|
||||
// prPoolDestroy(&pool);
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
typedef struct PrPool PrPool;
|
||||
|
||||
struct PrPool {
|
||||
void **blocks; // array of allocated block pointers (for destroy)
|
||||
u64 block_count; // number of allocated blocks
|
||||
u64 block_cap; // capacity of blocks array
|
||||
void *free_list; // intrusive free list head
|
||||
u64 slot_size; // user-requested slot size
|
||||
u64 alloc_size; // actual slot size used internally (>= slot_size, >= sizeof(void*))
|
||||
u64 block_slots; // slots per block
|
||||
u64 total; // total slots ever allocated (diagnostics)
|
||||
u64 active; // currently in use (diagnostics)
|
||||
};
|
||||
|
||||
// Initialise a pool.
|
||||
// slot_size: fixed size of each slot
|
||||
// initial_slots: starting capacity in slots (also used as block size)
|
||||
void prPoolInit(PrPool *pool, u64 slot_size, u64 initial_slots);
|
||||
|
||||
// Allocate one slot. Grows by a new block if the free list is empty.
|
||||
// Returns NULL only on allocation failure.
|
||||
void *prPoolAlloc(PrPool *pool);
|
||||
|
||||
// Return a slot to the pool's free list. Safe no-op on NULL.
|
||||
void prPoolFree(PrPool *pool, void *slot);
|
||||
|
||||
// Free all blocks and zero the pool.
|
||||
void prPoolDestroy(PrPool *pool);
|
||||
|
||||
// Diagnostics
|
||||
u64 prPoolTotalSlots(const PrPool *pool);
|
||||
u64 prPoolActiveSlots(const PrPool *pool);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !PR_POOL_ALLOCATOR_H
|
||||
Reference in New Issue
Block a user