aboutsummaryrefslogtreecommitdiff
path: root/mempool/include/mempool.h
blob: a0b3a331f0b4c7b260b37dc7115e6ed583f57a53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#pragma once

#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/// Define a typed mempool of the given number of blocks.
#define DEF_MEMPOOL(POOL, TYPE, NUM_BLOCKS) \
  typedef struct POOL {                     \
    mempool   pool;                         \
    BlockInfo block_info[NUM_BLOCKS];       \
    TYPE      blocks[NUM_BLOCKS];           \
  } POOL;

/// Create a new pool.
#define mempool_make(POOL)                                             \
  {                                                                    \
    assert(POOL);                                                      \
    const size_t block_size = sizeof((POOL)->blocks[0]);               \
    const size_t num_blocks = sizeof((POOL)->blocks) / block_size;     \
    mempool_make_(                                                     \
        &(POOL)->pool, (POOL)->block_info, (POOL)->blocks, num_blocks, \
        block_size);                                                   \
  }

/// Allocate a new block.
/// Return 0 if there is no memory left.
#define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool)

/// Free the block.
/// The block pointer is conveniently set to 0.
#define mempool_free(POOL, BLOCK_PTR) \
  assert(*BLOCK_PTR);                 \
  mempool_free_(&(POOL)->pool, (void**)BLOCK_PTR)

/// Return the ith block.
/// The block must have been allocated.
#define mempool_get_block(POOL, INDEX) \
  ((__typeof__((POOL)->blocks[0])*)mempool_get_block_(&(POOL)->pool, INDEX))

/// Get the index to the given block.
#define mempool_get_block_index(POOL, BLOCK_PTR) \
  mempool_get_block_index_(&(POOL)->pool, BLOCK_PTR)

/// Iterate over the used blocks of the pool.
///
/// The caller can use 'i' as the index of the current block.
///
/// It is valid to mempool_free() the object at each step of the iteration.
#define mempool_foreach(POOL, ITER, BODY)                                    \
  for (size_t i = 0;                                                         \
       i < (sizeof((POOL)->blocks) / sizeof(__typeof__((POOL)->blocks[0]))); \
       ++i) {                                                                \
    if (!(POOL)->block_info[i].used) {                                       \
      continue;                                                              \
    }                                                                        \
    __typeof__((POOL)->blocks[0])* ITER = &(POOL)->blocks[i];                \
    (void)ITER;                                                              \
    BODY;                                                                    \
  }

typedef struct BlockInfo {
  bool used;
} BlockInfo;

typedef struct mempool {
  size_t     block_size_bytes;
  size_t     num_blocks;
  size_t     next_free_block;
  bool       full;
  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.
/// All blocks are zeroed out for convenience.
void 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.
size_t mempool_get_block_index_(const mempool*, const void* block);