1#include "alloc.h"
2
3#include <stdint.h>
4#include <string.h>
5
6#define ARENA_SIZE (1024u * 1024u)
7#define ALIGNMENT 16u
8#define MIN_PAYLOAD 16u
9#define FOOTER_SIZE 16u
10#define CANARY_MAGIC UINT64_C(0x9e3779b97f4a7c15)
11
12#define ALIGN_UP(x) (((x) + (ALIGNMENT - 1u)) & ~(size_t)(ALIGNMENT - 1u))
13
14typedef struct block block_t;
15struct block {
16 size_t size;
17 int free;
18 uint64_t canary;
19 block_t *prev;
20 block_t *next;
21 block_t *free_prev;
22 block_t *free_next;
23};
24
25#define HEADER_SIZE ALIGN_UP(sizeof(block_t))
26#define OVERHEAD (HEADER_SIZE + FOOTER_SIZE)
27
28static _Alignas(16) unsigned char arena[ARENA_SIZE];
29static block_t *heap_head;
30static block_t *free_head;
31static int initialized;
32
33static uint64_t block_canary(const block_t *b) {
34 uintptr_t p = (uintptr_t)b;
35 return (uint64_t)(p >> 4) ^ (uint64_t)b->size ^ CANARY_MAGIC;
36}
37
38static uint64_t *footer_ptr(const block_t *b) {
39 return (uint64_t *)((unsigned char *)b + HEADER_SIZE + b->size);
40}
41
42static void write_canaries(block_t *b) {
43 b->canary = block_canary(b);
44 *footer_ptr(b) = b->canary;
45}
46
47static int block_canaries_ok(const block_t *b) {
48 if (b->canary != block_canary(b)) return 0;
49 return *footer_ptr(b) == b->canary;
50}
51
52static void free_list_remove(block_t *b) {
53 if (b->free_prev) b->free_prev->free_next = b->free_next;
54 else if (free_head == b) free_head = b->free_next;
55 if (b->free_next) b->free_next->free_prev = b->free_prev;
56 b->free_prev = NULL;
57 b->free_next = NULL;
58}
59
60static void free_list_push(block_t *b) {
61 b->free = 1;
62 b->free_prev = NULL;
63 b->free_next = free_head;
64 if (free_head) free_head->free_prev = b;
65 free_head = b;
66}
67
68static void init_heap(void) {
69 if (initialized) return;
70 heap_head = (block_t *)arena;
71 heap_head->size = ALIGN_UP(ARENA_SIZE - OVERHEAD);
72 heap_head->free = 1;
73 heap_head->prev = NULL;
74 heap_head->next = NULL;
75 heap_head->free_prev = NULL;
76 heap_head->free_next = NULL;
77 write_canaries(heap_head);
78 free_head = heap_head;
79 initialized = 1;
80}
81
82static void split_block(block_t *b, size_t need) {
83 size_t remaining = b->size - need;
84 if (remaining < OVERHEAD + MIN_PAYLOAD) return;
85
86 block_t *n = (block_t *)((unsigned char *)b + HEADER_SIZE + need + FOOTER_SIZE);
87 n->size = remaining - OVERHEAD;
88 n->free = 1;
89 n->prev = b;
90 n->next = b->next;
91 if (n->next) n->next->prev = n;
92 n->free_prev = NULL;
93 n->free_next = NULL;
94
95 b->size = need;
96 b->next = n;
97 write_canaries(b);
98 write_canaries(n);
99 free_list_push(n);
100 (void)coalesce(n);
101}
102
103static block_t *coalesce(block_t *b) {
104 if (b->next && b->next->free) {
105 block_t *n = b->next;
106 free_list_remove(n);
107 b->size += OVERHEAD + n->size;
108 b->next = n->next;
109 if (b->next) b->next->prev = b;
110 write_canaries(b);
111 }
112 if (b->prev && b->prev->free) {
113 block_t *p = b->prev;
114 free_list_remove(b);
115 p->size += OVERHEAD + b->size;
116 p->next = b->next;
117 if (p->next) p->next->prev = p;
118 write_canaries(p);
119 b = p;
120 }
121 return b;
122}
123
124void *my_malloc(size_t size) {
125 init_heap();
126 if (size == 0) return NULL;
127 size_t need = ALIGN_UP(size);
128 if (need < size) return NULL;
129
130 for (block_t *b = free_head; b; b = b->free_next) {
131 if (b->size >= need) {
132 if (!block_canaries_ok(b)) return NULL;
133 free_list_remove(b);
134 b->free = 0;
135 split_block(b, need);
136 write_canaries(b);
137 return (unsigned char *)b + HEADER_SIZE;
138 }
139 }
140 return NULL;
141}
142
143void my_free(void *ptr) {
144 if (!ptr) return;
145 init_heap();
146 block_t *b = (block_t *)((unsigned char *)ptr - HEADER_SIZE);
147 if ((unsigned char *)b < arena || (unsigned char *)b >= arena + ARENA_SIZE) return;
148 if (!block_canaries_ok(b) || b->free) return;
149 b->free = 1;
150 write_canaries(b);
151 free_list_push(b);
152 (void)coalesce(b);
153}
154
155void *my_calloc(size_t nmemb, size_t size) {
156 if (size != 0 && nmemb > (size_t)-1 / size) return NULL;
157 size_t total = nmemb * size;
158 void *p = my_malloc(total);
159 if (p) memset(p, 0, total);
160 return p;
161}
162
163void *my_realloc(void *ptr, size_t size) {
164 if (!ptr) return my_malloc(size);
165 if (size == 0) {
166 my_free(ptr);
167 return NULL;
168 }
169
170 init_heap();
171 block_t *b = (block_t *)((unsigned char *)ptr - HEADER_SIZE);
172 if (!block_canaries_ok(b) || b->free) return NULL;
173 size_t need = ALIGN_UP(size);
174 if (need < size) return NULL;
175
176 if (need <= b->size) {
177 split_block(b, need);
178 write_canaries(b);
179 return ptr;
180 }
181
182 if (b->next && b->next->free && b->size + OVERHEAD + b->next->size >= need) {
183 block_t *n = b->next;
184 free_list_remove(n);
185 b->size += OVERHEAD + n->size;
186 b->next = n->next;
187 if (b->next) b->next->prev = b;
188 write_canaries(b);
189 split_block(b, need);
190 write_canaries(b);
191 return (unsigned char *)b + HEADER_SIZE;
192 }
193
194 void *np = my_malloc(size);
195 if (!np) return NULL;
196 memcpy(np, ptr, b->size < size ? b->size : size);
197 my_free(ptr);
198 return np;
199}
200
201int my_heap_check(void) {
202 init_heap();
203 unsigned char *expected = arena;
204 block_t *prev = NULL;
205 for (block_t *b = heap_head; b; b = b->next) {
206 if ((unsigned char *)b != expected) return 0;
207 if (((uintptr_t)b & (ALIGNMENT - 1u)) != 0) return 0;
208 if (b->prev != prev) return 0;
209 if ((b->size & (ALIGNMENT - 1u)) != 0) return 0;
210 if ((unsigned char *)b + OVERHEAD + b->size > arena + ARENA_SIZE) return 0;
211 if (!block_canaries_ok(b)) return 0;
212 if (b->free && b->next && b->next->free) return 0;
213 expected = (unsigned char *)b + OVERHEAD + b->size;
214 prev = b;
215 }
216 if (expected != arena + ARENA_SIZE) return 0;
217
218 for (block_t *f = free_head; f; f = f->free_next) {
219 if (!f->free) return 0;
220 if (f->free_next && f->free_next->free_prev != f) return 0;
221 }
222 return 1;
223}
224
225my_stats_t my_stats(void) {
226 init_heap();
227 my_stats_t s = {0, 0, 0};
228 for (block_t *b = heap_head; b; b = b->next) {
229 if (b->free) {
230 s.free_block_count++;
231 if (b->size > s.largest_free_block) s.largest_free_block = b->size;
232 } else {
233 s.bytes_in_use += b->size;
234 }
235 }
236 return s;
237}
238
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.