MULE
0.0.1
Multi-Use Library Essentials
Toggle main menu visibility
Loading...
Searching...
No Matches
mem.c
1
#include "mem/mem.h"
2
3
#include <assert.h>
4
#include <stdlib.h>
5
#include <string.h>
6
7
struct
ArenaBlock
{
8
ArenaBlock
*
prev
;
/**< Older block, or null. */
9
size_t
used
;
/**< Bytes of `data` handed out. */
10
size_t
cap
;
/**< Bytes in `data`. */
11
alignas
(max_align_t)
char
data
[];
/**< The memory itself. */
12
};
13
14
void
*
mem_realloc
(
const
Allocator
*a,
void
*old,
size_t
old_size,
15
size_t
new_size) {
16
if
(a != NULL)
return
a->
fn
(a->
ctx
, old, old_size, new_size);
17
/* realloc(p, 0) is undefined in C23, so freeing is its own branch. */
18
if
(new_size == 0) {
19
free(old);
20
return
NULL;
21
}
22
#ifdef __APPLE__
23
/* macOS 26 keeps a large block grown by realloc dirty after free, so
24
* growing leaked memory. A fresh block and a copy returns it. */
25
if
(old != NULL && new_size > old_size) {
26
void
*p = malloc(new_size);
27
if
(p == NULL)
return
NULL;
28
memcpy(p, old, old_size);
29
free(old);
30
return
p;
31
}
32
#endif
33
return
realloc(old, new_size);
34
}
35
36
/* Carve `size` bytes out of `b`, or return null if they do not fit. */
37
static
void
*mem_bump_(
ArenaBlock
*b,
size_t
size,
size_t
align) {
38
size_t
pad = (size_t)-(uintptr_t)(b->
data
+ b->
used
) & (align - 1);
39
size_t
start, end;
40
if
(ckd_add(&start, b->
used
, pad) ||
41
ckd_add(&end, start, size) ||
42
end > b->
cap
)
43
return
NULL;
44
b->
used
= end;
45
return
b->
data
+ start;
46
}
47
48
void
*
arena_alloc
(
Arena
*a,
size_t
size,
size_t
align) {
49
assert(align != 0 && (align & (align - 1)) == 0);
50
void
*p = a->
head
!= NULL ? mem_bump_(a->
head
, size, align) : NULL;
51
if
(p == NULL) {
52
/* Room for the worst-case padding too, so the bump cannot fail. */
53
size_t
cap, bytes;
54
if
(ckd_add(&cap, size, align - 1))
return
NULL;
55
if
(cap <
MEM_ARENA_BLOCK
) cap =
MEM_ARENA_BLOCK
;
56
if
(ckd_add(&bytes, cap,
sizeof
(
ArenaBlock
)))
return
NULL;
57
58
ArenaBlock
*b = malloc(bytes);
59
if
(b == NULL)
return
NULL;
60
*b = (
ArenaBlock
){ .prev = a->
head
, .
cap
= cap };
61
a->
head
= b;
62
p = mem_bump_(b, size, align);
63
}
64
a->
last
= p;
65
return
p;
66
}
67
68
static
void
*mem_arena_fn_(
void
*ctx,
void
*old,
size_t
old_size,
69
size_t
new_size) {
70
Arena
*a = ctx;
71
72
/* The newest allocation ends at head->used, so it can move that mark. */
73
if
(old != NULL && old == a->
last
) {
74
ArenaBlock
*b = a->
head
;
75
size_t
start = (size_t)((
char
*)old - b->
data
);
76
size_t
end;
77
if
(new_size == 0) {
78
b->
used
= start;
79
a->
last
= NULL;
80
return
NULL;
81
}
82
if
(!ckd_add(&end, start, new_size) && end <= b->cap) {
83
b->
used
= end;
84
return
old;
85
}
86
}
87
88
if
(new_size == 0)
return
NULL;
/* older allocations wait for reset */
89
void
*p =
arena_alloc
(a, new_size,
alignof
(max_align_t));
90
if
(p != NULL && old != NULL)
91
memcpy(p, old, old_size < new_size ? old_size : new_size);
92
return
p;
93
}
94
95
const
Allocator
*
arena_allocator
(
Arena
*a) {
96
a->
allocator
= (
Allocator
){ .fn = mem_arena_fn_, .ctx = a };
97
return
&a->
allocator
;
98
}
99
100
void
arena_reset
(
Arena
*a) {
101
if
(a->
head
== NULL)
return
;
102
for
(
ArenaBlock
*b = a->
head
->
prev
; b != NULL;) {
103
ArenaBlock
*prev = b->
prev
;
104
free(b);
105
b = prev;
106
}
107
a->
head
->
prev
= NULL;
108
a->
head
->
used
= 0;
109
a->
last
= NULL;
110
}
111
112
void
arena_free
(
Arena
*a) {
113
for
(
ArenaBlock
*b = a->
head
; b != NULL;) {
114
ArenaBlock
*prev = b->
prev
;
115
free(b);
116
b = prev;
117
}
118
/* Keep the allocator, so Strs that point at it can still allocate. */
119
*a = (
Arena
){ .allocator = a->
allocator
};
120
}
Allocator::fn
AllocatorFn * fn
Called with ctx and the mem_realloc arguments.
Definition
mem.h:57
Arena::allocator
Allocator allocator
Filled by arena_allocator().
Definition
mem.h:89
Allocator::ctx
void * ctx
Passed to fn unchanged.
Definition
mem.h:58
Arena::head
ArenaBlock * head
Newest block.
Definition
mem.h:87
Arena::last
void * last
Most recent allocation, for in-place growth.
Definition
mem.h:88
mem_realloc
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
MEM_ARENA_BLOCK
#define MEM_ARENA_BLOCK
Bytes in a regular arena block.
Definition
mem.h:79
arena_reset
void arena_reset(Arena *a)
Forget every allocation and keep the newest block for reuse.
Definition
mem.c:100
arena_allocator
const Allocator * arena_allocator(Arena *a)
An Allocator that draws from a.
Definition
mem.c:95
arena_alloc
void * arena_alloc(Arena *a, size_t size, size_t align)
Hand out size bytes from a.
Definition
mem.c:48
arena_free
void arena_free(Arena *a)
Release every block.
Definition
mem.c:112
Allocator
Where a module gets its memory.
Definition
mem.h:56
Arena
Bump allocator over a chain of blocks.
Definition
mem.h:86
ArenaBlock
One block of an arena.
Definition
mem.c:7
ArenaBlock::data
char data[]
The memory itself.
Definition
mem.c:11
ArenaBlock::prev
ArenaBlock * prev
Older block, or null.
Definition
mem.c:8
ArenaBlock::used
size_t used
Bytes of data handed out.
Definition
mem.c:9
ArenaBlock::cap
size_t cap
Bytes in data.
Definition
mem.c:10
src
mem
mem.c
Generated by
1.18.0