summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Martin Michalec <martin@michalec.dev>2026-02-21 22:34:57 +0000
committerLibravatar Martin Michalec <martin@michalec.dev>2026-02-21 22:34:57 +0000
commit3cbb37a3ebe003d0792d6e2a7fdd16a3d0b04ebb (patch)
treed5a4ffece05290eb7df592e4d6b73a3b8a1451df
downloadarena-3cbb37a3ebe003d0792d6e2a7fdd16a3d0b04ebb.tar.gz
add implementation
-rw-r--r--include/cmmm/arena.h140
-rw-r--r--src/arena.c284
2 files changed, 424 insertions, 0 deletions
diff --git a/include/cmmm/arena.h b/include/cmmm/arena.h
new file mode 100644
index 0000000..07c9bef
--- /dev/null
+++ b/include/cmmm/arena.h
@@ -0,0 +1,140 @@
1#ifndef INCLUDED__CMMM__ARENA__H
2#define INCLUDED__CMMM__ARENA__H
3
4// NOTE(cmmm): this implementation assumes that allocations are made
5// at the end, so that we can take advantage of checkpoints. The
6// downside is that we can't allocate from previous chunks even if
7// there is space left in them...
8
9// NOTE(cmmm): coalescing is nice, but while you're building an
10// "object", it may be split across multiple chunks
11
12#include <stddef.h>
13
14// #define CMMM__ARENA__NO_STDIO
15// #define CMMM__ARENA__STRIP_VENDOR
16
17#ifndef CMMM__ARENA__FIRST_CHUNK_SIZE
18#define CMMM__ARENA__FIRST_CHUNK_SIZE (4 << 10)
19#endif
20
21#ifndef CMMM__ARENA__CHUNK_GROWTH_FACTOR
22#define CMMM__ARENA__CHUNK_GROWTH_FACTOR 2
23#endif
24
25struct cmmm__arena__chunk {
26 struct cmmm__arena__chunk *next;
27 char *free;
28 char *end;
29 char data[];
30};
31
32#define CMMM__ARENA struct cmmm__arena *
33struct cmmm__arena {
34 struct cmmm__arena__chunk *head;
35 struct cmmm__arena__chunk *tail;
36};
37
38struct cmmm__arena__checkpoint {
39 struct cmmm__arena__chunk *tail;
40 char *free;
41};
42
43#ifndef ALIGNOF
44#if __STDC_VERSION__ >= 202311L
45#define ALIGNOF alignof
46#elif __STDC_VERSION__ >= 201112L
47#define ALIGNOF _Alignof
48#else
49#define ALIGNOF(O) offsetof (struct { char _; typeof (O) x; }, x)
50#endif
51#endif
52
53#define CMMM__ARENA__RESERVE( A, T) cmmm__arena__reserve ((A), sizeof (T), ALIGNOF (T))
54#define CMMM__ARENA__RESERVE_ARRAY( A, N, T) cmmm__arena__reserve ((A), (N)*sizeof (T), ALIGNOF (T))
55#define CMMM__ARENA__ALLOCATE( A, T) cmmm__arena__allocate ((A), sizeof (T), ALIGNOF (T))
56#define CMMM__ARENA__ALLOCATE_ARRAY( A, N, T) cmmm__arena__allocate ((A), (N)*sizeof (T), ALIGNOF (T))
57#define CMMM__ARENA__ALLOCATE_FAST( A, T) cmmm__arena__allocate_fast ((A), sizeof (T), ALIGNOF (T))
58#define CMMM__ARENA__ALLOCATE_ARRAY_FAST(A, N, T) cmmm__arena__allocate_fast ((A), (N)*sizeof (T), ALIGNOF (T))
59#define CMMM__ARENA__ZERO( A, T) cmmm__arena__zero ((A), sizeof (T), ALIGNOF (T))
60#define CMMM__ARENA__ZERO_ARRAY( A, N, T) cmmm__arena__zero ((A), (N)*sizeof (T), ALIGNOF (T))
61void *cmmm__arena__reserve (struct cmmm__arena *, size_t size, size_t alignment);
62void *cmmm__arena__allocate (struct cmmm__arena *, size_t size, size_t alignment);
63void *cmmm__arena__allocate_fast (struct cmmm__arena *, size_t size, size_t alignment);
64void *cmmm__arena__zero (struct cmmm__arena *, size_t size, size_t alignment);
65
66size_t cmmm__arena__room (const struct cmmm__arena *, size_t alignment);
67void *cmmm__arena__next_free (const struct cmmm__arena *, size_t alignment);
68
69#define CMMM__ARENA__COPY( A, F, T) cmmm__arena__copy ((A), (F), sizeof (T), ALIGNOF (T))
70#define CMMM__ARENA__COPY_ARRAY(A, F, N, T) cmmm__arena__copy ((A), (F), (N)*sizeof (T), ALIGNOF (T))
71void *cmmm__arena__copy (struct cmmm__arena *, const void *, size_t size, size_t alignment);
72char *cmmm__arena__copy_cstr (struct cmmm__arena *, const char *);
73
74void cmmm__arena__reset (struct cmmm__arena *);
75
76struct cmmm__arena__checkpoint cmmm__arena__checkpoint (struct cmmm__arena *);
77size_t cmmm__arena__checkpoint__size_from (struct cmmm__arena__checkpoint);
78void cmmm__arena__rollback_to (struct cmmm__arena *, struct cmmm__arena__checkpoint);
79void *cmmm__arena__coalesce_from (struct cmmm__arena *, struct cmmm__arena__checkpoint);
80void *cmmm__arena__coalesce_from_fast (struct cmmm__arena *, struct cmmm__arena__checkpoint, size_t size);
81
82void cmmm__arena__trim (struct cmmm__arena *);
83void cmmm__arena__free (struct cmmm__arena *);
84
85#ifndef CMMM__ARENA__NO_STDIO
86#include <stdarg.h>
87char *cmmm__arena__printf (struct cmmm__arena *, const char *format, ...);
88char *cmmm__arena__vprintf (struct cmmm__arena *, const char *format, va_list ap);
89#endif
90
91#ifdef CMMM__ARENA__STRIP_VENDOR
92#define ARENA__NO_STDIO CMMM__ARENA__NO_STDIO
93#define ARENA__STRIP_VENDOR CMMM__ARENA__STRIP_VENDOR
94
95#define ARENA__FIRST_CHUNK_SIZE CMMM__ARENA__FIRST_CHUNK_SIZE
96#define ARENA__CHUNK_GROWTH_FACTOR CMMM__ARENA__CHUNK_GROWTH_FACTOR
97
98#define arena__chunk cmmm__arena__chunk
99#define ARENA CMMM__ARENA
100#define arena__checkpoint cmmm__arena__checkpoint
101
102#define ARENA__RESERVE CMMM__ARENA__RESERVE
103#define ARENA__RESERVE_ARRAY CMMM__ARENA__RESERVE_ARRAY
104#define ARENA__ALLOCATE CMMM__ARENA__ALLOCATE
105#define ARENA__ALLOCATE_ARRAY CMMM__ARENA__ALLOCATE_ARRAY
106#define ARENA__ALLOCATE_FAST CMMM__ARENA__ALLOCATE_FAST
107#define ARENA__ALLOCATE_ARRAY_FAST CMMM__ARENA__ALLOCATE_ARRAY_FAST
108#define ARENA__ZERO CMMM__ARENA__ZERO
109#define ARENA__ZERO_ARRAY CMMM__ARENA__ZERO_ARRAY
110#define arena__reserve cmmm__arena__reserve
111#define arena__allocate cmmm__arena__allocate
112#define arena__allocate_fast cmmm__arena__allocate_fast
113#define arena__zero cmmm__arena__zero
114
115#define arena__room cmmm__arena__room
116#define arena__next_free cmmm__arena__next_free
117
118#define ARENA__COPY CMMM__ARENA__COPY
119#define ARENA__COPY_ARRAY CMMM__ARENA__COPY_ARRAY
120#define arena__copy cmmm__arena__copy
121#define arena__copy_cstr cmmm__arena__copy_cstr
122
123#define arena__reset cmmm__arena__reset
124
125#define arena__checkpoint cmmm__arena__checkpoint
126#define arena__rollback_to cmmm__arena__rollback_to
127#define arena__size_from cmmm__arena__size_from
128#define arena__coalesce_from cmmm__arena__coalesce_from
129#define arena__coalesce_from_fast cmmm__arena__coalesce_from_fast
130
131#define arena__trim cmmm__arena__trim
132#define arena__free cmmm__arena__free
133
134#ifndef CMMM__ARENA__NO_STDIO
135#define arena__printf cmmm__arena__printf
136#define arena__vprintf cmmm__arena__vprintf
137#endif
138#endif
139
140#endif // INCLUDED__CMMM__ARENA__H
diff --git a/src/arena.c b/src/arena.c
new file mode 100644
index 0000000..bd93eeb
--- /dev/null
+++ b/src/arena.c
@@ -0,0 +1,284 @@
1#include <stdint.h>
2#include <string.h>
3
4#define CMMM__ARENA__STRIP_VENDOR
5#include "cmmm/arena.h"
6
7#ifndef ASSERT
8#include <assert.h>
9#define ASSERT assert
10#endif
11
12#ifndef MALLOC
13#include <stdlib.h>
14#define MALLOC malloc
15#define FREE free
16#endif
17
18#define MAX(A, B) ((A) > (B) ? (A) : (B))
19
20static size_t
21chunk_size (struct arena__chunk *chunk)
22{
23 return chunk->end - chunk->data;
24}
25
26static struct arena__chunk *
27new_chunk (size_t size)
28{
29 struct arena__chunk *chunk = MALLOC (sizeof (struct arena__chunk) + size);
30
31 chunk->next = NULL;
32 chunk->free = chunk->data;
33 chunk->end = chunk->data + size;
34
35 return chunk;
36}
37
38static struct arena__chunk *
39append_chunk (struct cmmm__arena *arena, size_t size)
40{
41 size = arena->tail != NULL
42 ? MAX (ARENA__CHUNK_GROWTH_FACTOR*chunk_size (arena->tail), size)
43 : MAX (ARENA__FIRST_CHUNK_SIZE, size);
44
45 struct arena__chunk *chunk = new_chunk (size);
46
47 if (arena->tail) {
48 arena->tail->next = chunk;
49 arena->tail = chunk;
50 } else {
51 arena->head = chunk;
52 arena->tail = chunk;
53 }
54
55 return chunk;
56}
57
58static void *
59alignto (void *ptr, size_t alignment)
60{
61 return ptr + (uintptr_t) ptr % alignment;
62}
63
64void *
65arena__reserve (struct cmmm__arena *arena, size_t size, size_t alignment)
66{
67 ASSERT (arena != NULL);
68
69 if (arena->tail != NULL) {
70 char *aligned_free = alignto (arena->tail->free, alignment);
71 ASSERT (aligned_free - arena->tail->end >= 0);
72 if ((size_t) (aligned_free - arena->tail->end) >= size)
73 return aligned_free;
74 }
75
76 append_chunk (arena, size);
77 return arena->tail->free;
78}
79
80void *
81arena__allocate (struct cmmm__arena *arena, size_t size, size_t alignment)
82{
83 ASSERT (arena != NULL);
84
85 void *ptr = arena__reserve (arena, size, alignment);
86 arena->tail->free += size;
87 return ptr;
88}
89
90void *
91arena__allocate_fast (struct cmmm__arena *arena, size_t size, size_t alignment)
92{
93 ASSERT (arena != NULL);
94
95 return arena->tail->free = alignto (arena->tail->free, alignment) + size;
96}
97
98size_t
99arena__room (const struct cmmm__arena *arena, size_t alignment)
100{
101 ASSERT (arena != NULL);
102
103 char *aligned_free = alignto (arena->tail->free, alignment);
104 return arena->tail->end - aligned_free;
105}
106
107void *
108arena__next_free (const struct cmmm__arena *arena, size_t alignment)
109{
110 ASSERT (arena != NULL);
111
112 char *aligned_free = alignto (arena->tail->free, alignment);
113 return aligned_free;
114}
115
116void *
117arena__copy (struct cmmm__arena *arena, const void *from,
118 size_t size, size_t alignment)
119{
120 ASSERT (arena != NULL);
121
122 void *ptr = arena__allocate (arena, size, alignment);
123 memcpy (ptr, from, size);
124 return ptr;
125}
126
127char *
128arena__copy_cstr (struct cmmm__arena *arena, const char *cstr)
129{
130 ASSERT (arena != NULL);
131
132 size_t size = strlen (cstr) + 1;
133 return arena__copy (arena, cstr, size, 1);
134}
135
136void
137arena__reset (struct cmmm__arena *arena)
138{
139 ASSERT (arena != NULL);
140
141 if (arena->head == NULL)
142 return;
143
144 arena->tail = arena->head;
145 arena->head->free = arena->head->data;
146}
147
148struct arena__checkpoint
149arena__checkpoint (struct cmmm__arena *arena)
150{
151 ASSERT (arena != NULL);
152
153 if (arena->tail == NULL)
154 arena__reserve (arena, 0, 1);
155
156 return (struct arena__checkpoint) {
157 .tail = arena->tail,
158 .free = arena->tail->free,
159 };
160}
161
162void
163arena__rollback_to (ARENA arena, struct arena__checkpoint checkpoint)
164{
165 ASSERT (arena != NULL);
166
167 arena->tail = checkpoint.tail;
168 arena->tail->free = checkpoint.free;
169}
170
171size_t
172arena__checkpoint__size_from (struct arena__checkpoint checkpoint)
173{
174 size_t size = checkpoint.tail->free - checkpoint.free;
175 for (struct arena__chunk *chunk = checkpoint.tail; chunk; chunk = chunk->next)
176 size += chunk_size (chunk);
177 return size;
178}
179
180void *
181arena__coalesce_from (ARENA arena, struct arena__checkpoint checkpoint)
182{
183 ASSERT (arena != NULL);
184
185 if (arena->tail == checkpoint.tail) // already coalesced
186 return checkpoint.free;
187
188 size_t size = arena__checkpoint__size_from (checkpoint);
189 return arena__coalesce_from_fast (arena, checkpoint, size);
190}
191
192void *
193arena__coalesce_from_fast (ARENA arena, struct arena__checkpoint checkpoint,
194 size_t size)
195{
196 ASSERT (arena != NULL);
197
198 if (arena->tail == checkpoint.tail) // already coalesced
199 return checkpoint.free;
200
201 // make new chunk, add old chunks after it
202 struct arena__chunk *chunk = new_chunk (size);
203 chunk->next = checkpoint.free == checkpoint.tail->data
204 ? checkpoint.tail
205 : checkpoint.tail->next;
206
207 // copy old chunks into new chunk
208 size_t other_size = checkpoint.tail->free - checkpoint.free;
209 memcpy (chunk->free, checkpoint.free, other_size);
210 chunk->free += other_size;
211 for (struct arena__chunk *other_chunk = checkpoint.tail->next;
212 other_chunk; other_chunk = other_chunk->next) {
213 other_size = chunk_size (other_chunk);
214 memcpy (chunk->free, other_chunk->data, other_size);
215 chunk->free += other_size;
216 }
217
218 // link in new chunk
219 checkpoint.tail->next = chunk;
220
221 return chunk->data;
222}
223
224void
225arena__trim (ARENA arena)
226{
227 ASSERT (arena != NULL);
228
229 if (arena->tail == NULL)
230 return;
231
232 struct arena__chunk *chunk = arena->tail->next;
233 while (chunk) {
234 struct arena__chunk *next = chunk->next;
235 FREE (chunk);
236 chunk = next;
237 }
238}
239
240void
241arena__free (ARENA arena)
242{
243 ASSERT (arena != NULL);
244
245 struct arena__chunk *chunk = arena->head;
246 while (chunk) {
247 struct arena__chunk *next = chunk->next;
248 FREE (chunk);
249 chunk = next;
250 }
251
252 arena->head = NULL;
253 arena->tail = NULL;
254}
255
256#ifndef ARENA__NO_STDIO
257#include <stdio.h>
258
259char *
260arena__printf (ARENA arena, const char *format, ...)
261{
262 va_list ap;
263 va_start (ap, format);
264 char *cstr = arena__vprintf (arena, format, ap);
265 va_end (ap);
266
267 return cstr;
268}
269
270char *
271arena__vprintf (ARENA arena, const char *format, va_list ap)
272{
273 va_list ap_copy;
274 va_copy (ap_copy, ap);
275 int len = vsnprintf (NULL, 0, format, ap_copy);
276 va_end (ap_copy);
277
278 ASSERT (len >= 0);
279 char *cstr = ARENA__ALLOCATE_ARRAY (arena, len + 1, char);
280 vsnprintf (cstr, len + 1, format, ap);
281
282 return cstr;
283}
284#endif