From 2a016de1c2eb45fc5f9c8cebf6b3c726b01ec340 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Tue, 11 Jul 2023 18:37:31 -0700 Subject: Add support for dynamically allocated mempools. --- mempool/include/mempool.h | 76 +++++++++++++++++++++++++++++++++++------------ mempool/src/mempool.c | 44 ++++++++++++++++++++++----- 2 files changed, 94 insertions(+), 26 deletions(-) diff --git a/mempool/include/mempool.h b/mempool/include/mempool.h index a0b3a33..2447884 100644 --- a/mempool/include/mempool.h +++ b/mempool/include/mempool.h @@ -1,3 +1,16 @@ +/* + * Block/Pool Allocator. + * + * Clients should use the macros to define and use pools. They make the API + * type-safe. + * + * The pool allocator works on one big chunk of memory, which can be statically + * or dynamically allocated. This chunk is divided into fixed-sized blocks. + * Allocation/deallocation happens with block granularity. + * + * Block information is stored in a separate array so that client data is + * contiguous in the main pool of memory and better cached. + */ #pragma once #include @@ -5,7 +18,7 @@ #include #include -/// Define a typed mempool of the given number of blocks. +/// Define a statically-allocated, typed pool of the given number of blocks. #define DEF_MEMPOOL(POOL, TYPE, NUM_BLOCKS) \ typedef struct POOL { \ mempool pool; \ @@ -13,7 +26,15 @@ TYPE blocks[NUM_BLOCKS]; \ } POOL; -/// Create a new pool. +/// Define a dynamically-allocated, typed pool. +#define DEF_MEMPOOL_DYN(POOL, TYPE) \ + typedef struct POOL { \ + mempool pool; \ + BlockInfo* block_info; \ + TYPE* blocks; \ + } POOL; + +/// Initialize a statically-allocated pool. #define mempool_make(POOL) \ { \ assert(POOL); \ @@ -24,8 +45,24 @@ block_size); \ } +/// Initialize a dynamically-allocated pool. +#define mempool_make_dyn(POOL, num_blocks, block_size) \ + mempool_make_(&(POOL)->pool, 0, 0, num_blocks, block_size) + +/// Destroy the pool. +/// +/// If the pool is dynamically allocated, then this function frees its memory. +#define mempool_del(POOL) mempool_del_(&(POOL)->pool) + +/// Clear the pool. +/// +/// This function frees all of the pool's blocks. The resulting pool is as if it +/// were newly created. +#define mempool_clear(POOL) mempool_clear_(&(POOL)->pool) + /// Allocate a new block. /// Return 0 if there is no memory left. +/// New blocks are conveniently zeroed out. #define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool) /// Free the block. @@ -60,6 +97,8 @@ BODY; \ } +// ----------------------------------------------------------------------------- + typedef struct BlockInfo { bool used; } BlockInfo; @@ -69,29 +108,28 @@ typedef struct mempool { size_t num_blocks; size_t next_free_block; bool full; + bool dynamic; // True if blocks and info are dynamically-allocated. BlockInfo* block_info; uint8_t* blocks; } mempool; -/// Create a pool allocator from user-provided memory. -/// `BlockInfo` must hold at least `num_blocks` entries. -/// `blocks` must be at least `num_blocks` * `block_size_bytes` bytes. +/// Create a pool allocator. +/// +/// 'BlockInfo' and 'blocks' may be user-provided (static pool) or null (dynamic +/// pool). +/// - If null, the pool malloc()s the memory for them. +/// - If given: +/// - `BlockInfo` must hold at least `num_blocks` entries. +/// - `blocks` must be at least `num_blocks` * `block_size_bytes` bytes. +/// /// All blocks are zeroed out for convenience. -void mempool_make_( +bool mempool_make_( mempool*, BlockInfo*, void* blocks, size_t num_blocks, size_t block_size_bytes); -/// Allocate a new block. -/// Return 0 if there is no memory left. -void* mempool_alloc_(mempool*); - -/// Free the block. -/// The block pointer is conveniently set to 0. -void mempool_free_(mempool*, void** block_ptr); - -/// Return the ith block. -/// The block must have been allocated. -void* mempool_get_block_(const mempool*, size_t block_index); - -/// Get the index to the given block. +void mempool_del_(mempool*); +void mempool_clear_(mempool*); +void* mempool_alloc_(mempool*); +void mempool_free_(mempool*, void** block_ptr); +void* mempool_get_block_(const mempool*, size_t block_index); size_t mempool_get_block_index_(const mempool*, const void* block); diff --git a/mempool/src/mempool.c b/mempool/src/mempool.c index b4693a5..059db93 100644 --- a/mempool/src/mempool.c +++ b/mempool/src/mempool.c @@ -1,24 +1,54 @@ #include "mempool.h" +#include #include static inline size_t min(size_t a, size_t b) { return a < b ? a : b; } -void mempool_make_( +bool mempool_make_( mempool* pool, BlockInfo* block_info, void* blocks, size_t num_blocks, size_t block_size_bytes) { assert(pool); - assert(block_info); - assert(blocks); + assert((block_info && blocks) || (!block_info && !blocks)); assert(num_blocks >= 1); pool->block_size_bytes = block_size_bytes; pool->num_blocks = num_blocks; pool->next_free_block = 0; pool->full = false; - pool->block_info = block_info; - pool->blocks = blocks; - memset(blocks, 0, num_blocks * block_size_bytes); - memset(block_info, 0, num_blocks * sizeof(BlockInfo)); + if (!block_info) { + block_info = calloc(num_blocks, sizeof(BlockInfo)); + blocks = calloc(num_blocks, block_size_bytes); + pool->dynamic = true; + } else { + memset(blocks, 0, num_blocks * block_size_bytes); + memset(block_info, 0, num_blocks * sizeof(BlockInfo)); + pool->dynamic = false; + } + pool->block_info = block_info; + pool->blocks = blocks; + return (block_info != 0) && (blocks != 0); +} + +void mempool_del_(mempool* pool) { + assert(pool); + if (pool->dynamic) { + if (pool->block_info) { + free(pool->block_info); + pool->block_info = 0; + } + if (pool->blocks) { + free(pool->blocks); + pool->blocks = 0; + } + } +} + +void mempool_clear_(mempool* pool) { + assert(pool); + pool->next_free_block = 0; + pool->full = false; + memset(pool->blocks, 0, pool->num_blocks * pool->block_size_bytes); + memset(pool->block_info, 0, pool->num_blocks * sizeof(BlockInfo)); } void* mempool_alloc_(mempool* pool) { -- cgit v1.2.3