1#include "alloc.h"
2
3#include <stdint.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27typedef struct block {
28 uint32_t head_canary;
29 uint32_t free;
30 size_t size;
31 struct block *prev_phys;
32 struct block *next_phys;
33 struct block *prev_free;
34 struct block *next_free;
35} block_t;
36
37typedef struct footer {
38 uint64_t canary;
39 uint64_t back;
40} footer_t;
41
42#define ARENA_SIZE ((size_t)(1u << 20))
43#define ALIGNMENT ((size_t)16)
44#define HEADER_SIZE (sizeof(block_t))
45#define FOOTER_SIZE (sizeof(footer_t))
46
47_Static_assert(HEADER_SIZE % ALIGNMENT == 0, "header must be 16-byte sized");
48_Static_assert(FOOTER_SIZE == 16, "footer must be 16 bytes");
49_Static_assert(ARENA_SIZE % ALIGNMENT == 0, "arena must be 16-byte sized");
50_Static_assert((ARENA_SIZE - HEADER_SIZE - FOOTER_SIZE) % ALIGNMENT == 0,
51 "initial payload must be 16-byte aligned");
52
53
54static _Alignas(16) unsigned char arena[ARENA_SIZE];
55static block_t *free_list;
56static int initialized;
57
58
59
60
61
62
63
64
65
66#define HEAD_MAGIC 0xA110C8EDu
67#define FOOT_MAGIC 0xDEADBEEFCAFEF00Dull
68
69static uint32_t head_canary_for(const block_t *b) {
70 return HEAD_MAGIC ^ (uint32_t)(uintptr_t)b;
71}
72
73static uint64_t foot_canary_for(const block_t *b) {
74 return FOOT_MAGIC ^ (uint64_t)(uintptr_t)b;
75}
76
77static footer_t *footer_of(block_t *b) {
78 return (footer_t *)((unsigned char *)b + HEADER_SIZE + b->size);
79}
80
81static void write_canaries(block_t *b) {
82 b->head_canary = head_canary_for(b);
83 footer_t *f = footer_of(b);
84 f->canary = foot_canary_for(b);
85 f->back = (uint64_t)(uintptr_t)b;
86}
87
88static int canaries_ok(block_t *b) {
89 if (b->head_canary != head_canary_for(b)) return 0;
90 footer_t *f = footer_of(b);
91 if (f->canary != foot_canary_for(b)) return 0;
92 if (f->back != (uint64_t)(uintptr_t)b) return 0;
93 return 1;
94}
95
96
97static void *payload_of(block_t *b) {
98 return (unsigned char *)b + HEADER_SIZE;
99}
100static block_t *block_of(void *ptr) {
101 return (block_t *)((unsigned char *)ptr - HEADER_SIZE);
102}
103static block_t *first_block(void) {
104 return (block_t *)arena;
105}
106
107#define ALIGN_UP(n, a) (((n) + ((a) - 1)) & ~((size_t)(a) - 1))
108
109static void fatal(const char *msg) {
110 fprintf(stderr, "my_alloc: fatal: %s\n", msg);
111 abort();
112}
113
114
115
116
117
118static void fl_insert(block_t *b) {
119 b->prev_free = NULL;
120 b->next_free = free_list;
121 if (free_list) free_list->prev_free = b;
122 free_list = b;
123}
124
125static void fl_remove(block_t *b) {
126 if (b->prev_free) b->prev_free->next_free = b->next_free;
127 else free_list = b->next_free;
128 if (b->next_free) b->next_free->prev_free = b->prev_free;
129 b->prev_free = b->next_free = NULL;
130}
131
132static void ensure_init(void) {
133 if (initialized) return;
134 initialized = 1;
135
136 block_t *b = first_block();
137 b->size = ARENA_SIZE - HEADER_SIZE - FOOTER_SIZE;
138 b->free = 1;
139 b->prev_phys = NULL;
140 b->next_phys = NULL;
141 b->prev_free = NULL;
142 b->next_free = NULL;
143 write_canaries(b);
144
145 free_list = NULL;
146 fl_insert(b);
147}
148
149
150
151
152
153
154
155
156static block_t *free_block(block_t *b) {
157 b->free = 1;
158
159
160 block_t *next = b->next_phys;
161 if (next && next->free) {
162 fl_remove(next);
163 b->size += FOOTER_SIZE + HEADER_SIZE + next->size;
164 b->next_phys = next->next_phys;
165 if (b->next_phys) b->next_phys->prev_phys = b;
166 }
167
168
169 block_t *prev = b->prev_phys;
170 if (prev && prev->free) {
171 fl_remove(prev);
172 prev->size += FOOTER_SIZE + HEADER_SIZE + b->size;
173 prev->next_phys = b->next_phys;
174 if (prev->next_phys) prev->next_phys->prev_phys = prev;
175 b = prev;
176 }
177
178 write_canaries(b);
179 fl_insert(b);
180 return b;
181}
182
183
184
185
186
187static void carve(block_t *b, size_t asize) {
188 if (b->size < asize + HEADER_SIZE + FOOTER_SIZE + ALIGNMENT)
189 return;
190
191 block_t *after = b->next_phys;
192 block_t *r = (block_t *)((unsigned char *)b + HEADER_SIZE + asize + FOOTER_SIZE);
193
194 r->size = b->size - asize - HEADER_SIZE - FOOTER_SIZE;
195 r->free = 0;
196 r->prev_phys = b;
197 r->next_phys = after;
198 r->prev_free = NULL;
199 r->next_free = NULL;
200 if (after) after->prev_phys = r;
201
202 b->next_phys = r;
203 b->size = asize;
204
205 free_block(r);
206}
207
208
209
210
211
212void *my_malloc(size_t size) {
213 if (size == 0 || size > ARENA_SIZE) return NULL;
214 ensure_init();
215
216 size_t asize = ALIGN_UP(size, ALIGNMENT);
217
218
219 block_t *b = free_list;
220 while (b && b->size < asize) b = b->next_free;
221 if (!b) return NULL;
222
223 fl_remove(b);
224 b->free = 0;
225 carve(b, asize);
226 write_canaries(b);
227 return payload_of(b);
228}
229
230void my_free(void *ptr) {
231 if (!ptr) return;
232 ensure_init();
233
234 if ((unsigned char *)ptr < arena + HEADER_SIZE ||
235 (unsigned char *)ptr >= arena + ARENA_SIZE)
236 fatal("free of a pointer outside the arena");
237
238 block_t *b = block_of(ptr);
239 if (!canaries_ok(b)) fatal("free of a corrupted or invalid block");
240 if (b->free) fatal("double free");
241
242 free_block(b);
243}
244
245void *my_calloc(size_t nmemb, size_t size) {
246 if (nmemb != 0 && size > ARENA_SIZE / nmemb) return NULL;
247 size_t total = nmemb * size;
248 void *p = my_malloc(total);
249 if (p) memset(p, 0, total);
250 return p;
251}
252
253void *my_realloc(void *ptr, size_t size) {
254 if (!ptr) return my_malloc(size);
255 if (size == 0) { my_free(ptr); return NULL; }
256 if (size > ARENA_SIZE) return NULL;
257 ensure_init();
258
259 block_t *b = block_of(ptr);
260 if (!canaries_ok(b)) fatal("realloc of a corrupted or invalid block");
261 if (b->free) fatal("realloc of a freed block");
262
263 size_t asize = ALIGN_UP(size, ALIGNMENT);
264
265
266 if (asize <= b->size) {
267 carve(b, asize);
268 write_canaries(b);
269 return ptr;
270 }
271
272
273
274 block_t *next = b->next_phys;
275 if (next && next->free &&
276 b->size + FOOTER_SIZE + HEADER_SIZE + next->size >= asize) {
277 fl_remove(next);
278 b->size += FOOTER_SIZE + HEADER_SIZE + next->size;
279 b->next_phys = next->next_phys;
280 if (b->next_phys) b->next_phys->prev_phys = b;
281 carve(b, asize);
282 write_canaries(b);
283 return ptr;
284 }
285
286
287 void *np = my_malloc(size);
288 if (!np) return NULL;
289 size_t copy = b->size < size ? b->size : size;
290 memcpy(np, ptr, copy);
291 my_free(ptr);
292 return np;
293}
294
295void my_stats(heap_stats_t *out) {
296 ensure_init();
297
298 out->bytes_in_use = 0;
299 out->used_blocks = 0;
300 out->free_blocks = 0;
301 out->free_bytes = 0;
302 out->largest_free_block = 0;
303
304 for (block_t *b = first_block(); b; b = b->next_phys) {
305 if (b->free) {
306 out->free_blocks++;
307 out->free_bytes += b->size;
308 if (b->size > out->largest_free_block)
309 out->largest_free_block = b->size;
310 } else {
311 out->used_blocks++;
312 out->bytes_in_use += b->size;
313 }
314 }
315}
316
317size_t my_heap_capacity(void) {
318 return ARENA_SIZE - HEADER_SIZE - FOOTER_SIZE;
319}
320
321int my_heap_check(void) {
322 ensure_init();
323
324 size_t total = 0;
325 size_t nblocks = 0;
326 size_t nfree_phys = 0;
327 block_t *prev = NULL;
328
329 for (block_t *b = first_block(); b; b = b->next_phys) {
330 unsigned char *bb = (unsigned char *)b;
331
332 if (bb < arena || bb + HEADER_SIZE > arena + ARENA_SIZE)
333 return HEAP_ERR_BOUNDS;
334 if (b->size % ALIGNMENT != 0)
335 return HEAP_ERR_ALIGN;
336
337 unsigned char *end = bb + HEADER_SIZE + b->size + FOOTER_SIZE;
338 if (end > arena + ARENA_SIZE)
339 return HEAP_ERR_BOUNDS;
340
341 if (b->head_canary != head_canary_for(b))
342 return HEAP_ERR_HEAD_CANARY;
343 {
344 footer_t *f = footer_of(b);
345 if (f->canary != foot_canary_for(b) ||
346 f->back != (uint64_t)(uintptr_t)b)
347 return HEAP_ERR_FOOT_CANARY;
348 }
349
350 if (b->prev_phys != prev)
351 return HEAP_ERR_PHYS_LINK;
352
353 block_t *computed_next = (block_t *)end;
354 if (end == arena + ARENA_SIZE) {
355 if (b->next_phys != NULL) return HEAP_ERR_PHYS_LINK;
356 } else if (b->next_phys != computed_next) {
357 return HEAP_ERR_PHYS_LINK;
358 }
359
360 if (b->free && prev && prev->free)
361 return HEAP_ERR_ADJACENT_FREE;
362
363 total += HEADER_SIZE + b->size + FOOTER_SIZE;
364 nblocks++;
365 if (b->free) nfree_phys++;
366 prev = b;
367 }
368
369 if (total != ARENA_SIZE)
370 return HEAP_ERR_TOTAL;
371
372
373 size_t nfree_list = 0;
374 size_t guard = nblocks + 1;
375 for (block_t *f = free_list; f; f = f->next_free) {
376 if (guard-- == 0) return HEAP_ERR_FREELIST;
377 unsigned char *fb = (unsigned char *)f;
378 if (fb < arena || fb + HEADER_SIZE + f->size + FOOTER_SIZE > arena + ARENA_SIZE)
379 return HEAP_ERR_FREELIST;
380 if (!f->free) return HEAP_ERR_FREELIST;
381 if (!canaries_ok(f)) return HEAP_ERR_FREELIST;
382 if (f->next_free && f->next_free->prev_free != f)
383 return HEAP_ERR_FREELIST;
384 nfree_list++;
385 }
386 if (nfree_list != nfree_phys)
387 return HEAP_ERR_FREELIST;
388
389 return HEAP_OK;
390}
391
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.