MULE 0.0.1
Multi-Use Library Essentials
Loading...
Searching...
No Matches
mem.h
1#pragma once
2/**
3 * @defgroup mod_mem Mem
4 * @brief The allocator interface, and an arena that frees everything at once.
5 *
6 * ## Allocators
7 *
8 * An Allocator is one realloc-shaped function and a context pointer. Every
9 * module that allocates takes a `const Allocator *`, and null means the libc
10 * heap. A Str or an Arr keeps the pointer in its `alloc` field, so the
11 * caller picks where its bytes live once and the rest of the code does not
12 * care.
13 *
14 * All allocation in the library goes through mem_realloc(). Only this module
15 * calls malloc, realloc and free.
16 *
17 * ## Arenas
18 *
19 * An Arena hands out memory from large blocks and frees all of it in one
20 * call. Use one for data that lives and dies together, such as everything
21 * built while handling one request or parsing one file.
22 *
23 * - A zero-initialised Arena (`Arena a = { 0 };`) is valid and owns nothing.
24 * The first allocation creates a block of at least #MEM_ARENA_BLOCK
25 * bytes.
26 * - arena_alloc() hands out raw aligned bytes. arena_allocator() wraps the
27 * arena in an Allocator, for a Str or an Arr to grow inside.
28 * - Growing the most recent allocation happens in place when the block has
29 * room, so a Str growing in an arena does not leave a copy behind at
30 * every doubling. Freeing the most recent allocation gives its bytes
31 * back. Older allocations stay until arena_reset() or arena_free().
32 *
33 * @snippet{trimleft} test_mem.c arena
34 *
35 * @warning An Arena must stay where it is once arena_allocator() has been
36 * called. The Allocator's context points at it, so copying or moving the
37 * Arena leaves every Str that uses it pointing at the old address.
38 * @{
39 */
40
41#include "base/base.h"
42
43/* Callers pass alignof(T) to arena_alloc. */
44#include <stdalign.h>
45
46/** @name Allocators
47 * The interface every allocating module takes.
48 * @{ */
49
50/** Allocate, resize or free, depending on @p old and @p new_size. It has
51 * the same contract as mem_realloc(), with @p ctx from the Allocator. */
52typedef void *AllocatorFn(void *ctx, void *old, size_t old_size,
53 size_t new_size);
54
55/** Where a module gets its memory. */
56typedef struct Allocator {
57 AllocatorFn *fn; /**< Called with `ctx` and the mem_realloc arguments. */
58 void *ctx; /**< Passed to `fn` unchanged. */
59} Allocator;
60
61/** Resize @p old from @p old_size to @p new_size bytes through @p a.
62 * @param a The allocator, or null for the libc heap.
63 * @param old The block to resize, or null to allocate a new one.
64 * @param old_size The size @p old was allocated with. Ignored when @p old
65 * is null.
66 * @param new_size The size wanted. Zero frees @p old.
67 * @return The new block, or null on failure with @p old untouched. A free
68 * returns null too, so `p = mem_realloc(a, p, n, 0);` frees and clears. */
69MULE_NODISCARD void *mem_realloc(const Allocator *a, void *old, size_t old_size,
70 size_t new_size);
71
72/** @} */
73
74/** @name Arenas
75 * Bump allocation from blocks that are freed together.
76 * @{ */
77
78/** Bytes in a regular arena block. Larger requests get a block of their own. */
79#define MEM_ARENA_BLOCK ((size_t)64 * 1024)
80
81/** One block of an arena. Defined in mem.c. */
82typedef struct ArenaBlock ArenaBlock;
83
84/** Bump allocator over a chain of blocks. Do not copy or move an Arena once
85 * arena_allocator() has been called on it. */
86typedef struct Arena {
87 ArenaBlock *head; /**< Newest block. Older blocks chain behind it. */
88 void *last; /**< Most recent allocation, for in-place growth. */
89 Allocator allocator; /**< Filled by arena_allocator(). */
90} Arena;
91
92/** Hand out @p size bytes from @p a.
93 * @param a The arena.
94 * @param size Bytes wanted.
95 * @param align A power of two, usually `alignof(T)`. An assert checks it.
96 * @return The bytes, or null when a new block cannot be allocated or the
97 * size overflows. */
98MULE_NODISCARD void *arena_alloc(Arena *a, size_t size, size_t align);
99/** An Allocator that draws from @p a. It lives as long as @p a.
100 * @warning @p a must not move after this call.
101 * @par Example
102 * @snippet{trimleft} test_mem.c allocator */
103MULE_NODISCARD const Allocator *arena_allocator(Arena *a);
104/** Forget every allocation and keep the newest block for reuse. Memory
105 * handed out before is invalid afterwards. */
106void arena_reset(Arena *a);
107/** Release every block. Safe to call twice, and @p a stays usable. Its
108 * Allocator still works, so a Str that points at it can allocate again. */
109void arena_free(Arena *a);
110
111/** @} */
112
113/** @} */
AllocatorFn * fn
Called with ctx and the mem_realloc arguments.
Definition mem.h:57
Allocator allocator
Filled by arena_allocator().
Definition mem.h:89
void * ctx
Passed to fn unchanged.
Definition mem.h:58
ArenaBlock * head
Newest block.
Definition mem.h:87
void * last
Most recent allocation, for in-place growth.
Definition mem.h:88
void * mem_realloc(const Allocator *a, void *old, size_t old_size, size_t new_size)
Resize old from old_size to new_size bytes through a.
Definition mem.c:14
void arena_reset(Arena *a)
Forget every allocation and keep the newest block for reuse.
Definition mem.c:100
const Allocator * arena_allocator(Arena *a)
An Allocator that draws from a.
Definition mem.c:95
void * arena_alloc(Arena *a, size_t size, size_t align)
Hand out size bytes from a.
Definition mem.c:48
void arena_free(Arena *a)
Release every block.
Definition mem.c:112
void * AllocatorFn(void *ctx, void *old, size_t old_size, size_t new_size)
Allocate, resize or free, depending on old and new_size.
Definition mem.h:52
Where a module gets its memory.
Definition mem.h:56
Bump allocator over a chain of blocks.
Definition mem.h:86
One block of an arena.
Definition mem.c:7