aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2023-07-11 18:37:31 -0700
committer3gg <3gg@shellblade.net>2023-07-11 18:37:31 -0700
commit2a016de1c2eb45fc5f9c8cebf6b3c726b01ec340 (patch)
tree17ca4f39a2777b201146d4c5f65f300a7ffeb045
parent08ec3a7a1fdb16cbb52b05f934bd001ca38bd991 (diff)
Add support for dynamically allocated mempools.
-rw-r--r--mempool/include/mempool.h76
-rw-r--r--mempool/src/mempool.c44
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 @@
1/*
2 * Block/Pool Allocator.
3 *
4 * Clients should use the macros to define and use pools. They make the API
5 * type-safe.
6 *
7 * The pool allocator works on one big chunk of memory, which can be statically
8 * or dynamically allocated. This chunk is divided into fixed-sized blocks.
9 * Allocation/deallocation happens with block granularity.
10 *
11 * Block information is stored in a separate array so that client data is
12 * contiguous in the main pool of memory and better cached.
13 */
1#pragma once 14#pragma once
2 15
3#include <assert.h> 16#include <assert.h>
@@ -5,7 +18,7 @@
5#include <stddef.h> 18#include <stddef.h>
6#include <stdint.h> 19#include <stdint.h>
7 20
8/// Define a typed mempool of the given number of blocks. 21/// Define a statically-allocated, typed pool of the given number of blocks.
9#define DEF_MEMPOOL(POOL, TYPE, NUM_BLOCKS) \ 22#define DEF_MEMPOOL(POOL, TYPE, NUM_BLOCKS) \
10 typedef struct POOL { \ 23 typedef struct POOL { \
11 mempool pool; \ 24 mempool pool; \
@@ -13,7 +26,15 @@
13 TYPE blocks[NUM_BLOCKS]; \ 26 TYPE blocks[NUM_BLOCKS]; \
14 } POOL; 27 } POOL;
15 28
16/// Create a new pool. 29/// Define a dynamically-allocated, typed pool.
30#define DEF_MEMPOOL_DYN(POOL, TYPE) \
31 typedef struct POOL { \
32 mempool pool; \
33 BlockInfo* block_info; \
34 TYPE* blocks; \
35 } POOL;
36
37/// Initialize a statically-allocated pool.
17#define mempool_make(POOL) \ 38#define mempool_make(POOL) \
18 { \ 39 { \
19 assert(POOL); \ 40 assert(POOL); \
@@ -24,8 +45,24 @@
24 block_size); \ 45 block_size); \
25 } 46 }
26 47
48/// Initialize a dynamically-allocated pool.
49#define mempool_make_dyn(POOL, num_blocks, block_size) \
50 mempool_make_(&(POOL)->pool, 0, 0, num_blocks, block_size)
51
52/// Destroy the pool.
53///
54/// If the pool is dynamically allocated, then this function frees its memory.
55#define mempool_del(POOL) mempool_del_(&(POOL)->pool)
56
57/// Clear the pool.
58///
59/// This function frees all of the pool's blocks. The resulting pool is as if it
60/// were newly created.
61#define mempool_clear(POOL) mempool_clear_(&(POOL)->pool)
62
27/// Allocate a new block. 63/// Allocate a new block.
28/// Return 0 if there is no memory left. 64/// Return 0 if there is no memory left.
65/// New blocks are conveniently zeroed out.
29#define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool) 66#define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool)
30 67
31/// Free the block. 68/// Free the block.
@@ -60,6 +97,8 @@
60 BODY; \ 97 BODY; \
61 } 98 }
62 99
100// -----------------------------------------------------------------------------
101
63typedef struct BlockInfo { 102typedef struct BlockInfo {
64 bool used; 103 bool used;
65} BlockInfo; 104} BlockInfo;
@@ -69,29 +108,28 @@ typedef struct mempool {
69 size_t num_blocks; 108 size_t num_blocks;
70 size_t next_free_block; 109 size_t next_free_block;
71 bool full; 110 bool full;
111 bool dynamic; // True if blocks and info are dynamically-allocated.
72 BlockInfo* block_info; 112 BlockInfo* block_info;
73 uint8_t* blocks; 113 uint8_t* blocks;
74} mempool; 114} mempool;
75 115
76/// Create a pool allocator from user-provided memory. 116/// Create a pool allocator.
77/// `BlockInfo` must hold at least `num_blocks` entries. 117///
78/// `blocks` must be at least `num_blocks` * `block_size_bytes` bytes. 118/// 'BlockInfo' and 'blocks' may be user-provided (static pool) or null (dynamic
119/// pool).
120/// - If null, the pool malloc()s the memory for them.
121/// - If given:
122/// - `BlockInfo` must hold at least `num_blocks` entries.
123/// - `blocks` must be at least `num_blocks` * `block_size_bytes` bytes.
124///
79/// All blocks are zeroed out for convenience. 125/// All blocks are zeroed out for convenience.
80void mempool_make_( 126bool mempool_make_(
81 mempool*, BlockInfo*, void* blocks, size_t num_blocks, 127 mempool*, BlockInfo*, void* blocks, size_t num_blocks,
82 size_t block_size_bytes); 128 size_t block_size_bytes);
83 129
84/// Allocate a new block. 130void mempool_del_(mempool*);
85/// Return 0 if there is no memory left. 131void mempool_clear_(mempool*);
86void* mempool_alloc_(mempool*); 132void* mempool_alloc_(mempool*);
87 133void mempool_free_(mempool*, void** block_ptr);
88/// Free the block. 134void* mempool_get_block_(const mempool*, size_t block_index);
89/// The block pointer is conveniently set to 0.
90void mempool_free_(mempool*, void** block_ptr);
91
92/// Return the ith block.
93/// The block must have been allocated.
94void* mempool_get_block_(const mempool*, size_t block_index);
95
96/// Get the index to the given block.
97size_t mempool_get_block_index_(const mempool*, const void* block); 135size_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 @@
1#include "mempool.h" 1#include "mempool.h"
2 2
3#include <stdlib.h>
3#include <string.h> 4#include <string.h>
4 5
5static inline size_t min(size_t a, size_t b) { return a < b ? a : b; } 6static inline size_t min(size_t a, size_t b) { return a < b ? a : b; }
6 7
7void mempool_make_( 8bool mempool_make_(
8 mempool* pool, BlockInfo* block_info, void* blocks, size_t num_blocks, 9 mempool* pool, BlockInfo* block_info, void* blocks, size_t num_blocks,
9 size_t block_size_bytes) { 10 size_t block_size_bytes) {
10 assert(pool); 11 assert(pool);
11 assert(block_info); 12 assert((block_info && blocks) || (!block_info && !blocks));
12 assert(blocks);
13 assert(num_blocks >= 1); 13 assert(num_blocks >= 1);
14 pool->block_size_bytes = block_size_bytes; 14 pool->block_size_bytes = block_size_bytes;
15 pool->num_blocks = num_blocks; 15 pool->num_blocks = num_blocks;
16 pool->next_free_block = 0; 16 pool->next_free_block = 0;
17 pool->full = false; 17 pool->full = false;
18 pool->block_info = block_info; 18 if (!block_info) {
19 pool->blocks = blocks; 19 block_info = calloc(num_blocks, sizeof(BlockInfo));
20 memset(blocks, 0, num_blocks * block_size_bytes); 20 blocks = calloc(num_blocks, block_size_bytes);
21 memset(block_info, 0, num_blocks * sizeof(BlockInfo)); 21 pool->dynamic = true;
22 } else {
23 memset(blocks, 0, num_blocks * block_size_bytes);
24 memset(block_info, 0, num_blocks * sizeof(BlockInfo));
25 pool->dynamic = false;
26 }
27 pool->block_info = block_info;
28 pool->blocks = blocks;
29 return (block_info != 0) && (blocks != 0);
30}
31
32void mempool_del_(mempool* pool) {
33 assert(pool);
34 if (pool->dynamic) {
35 if (pool->block_info) {
36 free(pool->block_info);
37 pool->block_info = 0;
38 }
39 if (pool->blocks) {
40 free(pool->blocks);
41 pool->blocks = 0;
42 }
43 }
44}
45
46void mempool_clear_(mempool* pool) {
47 assert(pool);
48 pool->next_free_block = 0;
49 pool->full = false;
50 memset(pool->blocks, 0, pool->num_blocks * pool->block_size_bytes);
51 memset(pool->block_info, 0, pool->num_blocks * sizeof(BlockInfo));
22} 52}
23 53
24void* mempool_alloc_(mempool* pool) { 54void* mempool_alloc_(mempool* pool) {