1#include "alloc.h"
2#include <string.h>
3
4#define ALIGNMENT 16
5#define CANARY_HEAD 0xCAFEBABE
6#define CANARY_FOOT 0xDEADBEEF
7
8typedef struct block_header {
9 uint32_t magic_head;
10 uint32_t is_free;
11 size_t size;
12 size_t req_size;
13 struct block_header *next_free;
14 struct block_header *prev_free;
15 uint64_t pad;
16} block_header_t;
17
18typedef struct block_footer {
19 uint32_t magic_foot;
20 uint32_t pad;
21 size_t size;
22} block_footer_t;
23
24#define HEADER_SIZE (sizeof(block_header_t))
25#define FOOTER_SIZE (sizeof(block_footer_t))
26#define MIN_PAYLOAD 16
27#define MIN_BLOCK_SIZE (HEADER_SIZE + MIN_PAYLOAD + FOOTER_SIZE)
28
29static uint8_t g_arena[ARENA_SIZE] __attribute__((aligned(16)));
30static block_header_t *g_free_list = NULL;
31static bool g_initialized = false;
32
33static inline size_t align_up(size_t size, size_t align) {
34 return (size + (align - 1)) & ~(align - 1);
35}
36
37static void free_list_add(block_header_t *block) {
38 block->next_free = g_free_list;
39 block->prev_free = NULL;
40 if (g_free_list != NULL) {
41 g_free_list->prev_free = block;
42 }
43 g_free_list = block;
44}
45
46static void free_list_remove(block_header_t *block) {
47 if (block->prev_free != NULL) {
48 block->prev_free->next_free = block->next_free;
49 } else {
50 g_free_list = block->next_free;
51 }
52 if (block->next_free != NULL) {
53 block->next_free->prev_free = block->prev_free;
54 }
55 block->next_free = NULL;
56 block->prev_free = NULL;
57}
58
59void my_init(void) {
60 block_header_t *initial_block = (block_header_t *)g_arena;
61 initial_block->magic_head = CANARY_HEAD;
62 initial_block->is_free = 1;
63 initial_block->size = ARENA_SIZE;
64 initial_block->req_size = 0;
65 initial_block->next_free = NULL;
66 initial_block->prev_free = NULL;
67 initial_block->pad = 0;
68
69 block_footer_t *initial_footer = (block_footer_t *)(g_arena + ARENA_SIZE - FOOTER_SIZE);
70 initial_footer->magic_foot = CANARY_FOOT;
71 initial_footer->pad = 0;
72 initial_footer->size = ARENA_SIZE;
73
74 g_free_list = initial_block;
75 g_initialized = true;
76}
77
78void *my_malloc(size_t size) {
79 if (size == 0) return NULL;
80 if (!g_initialized) my_init();
81
82 size_t payload_size = align_up(size, ALIGNMENT);
83 if (payload_size < MIN_PAYLOAD) {
84 payload_size = MIN_PAYLOAD;
85 }
86
87 size_t needed_block_size = HEADER_SIZE + payload_size + FOOTER_SIZE;
88 if (needed_block_size < size) return NULL;
89
90
91 block_header_t *curr = g_free_list;
92 while (curr != NULL) {
93 if (curr->size >= needed_block_size) {
94 break;
95 }
96 curr = curr->next_free;
97 }
98
99 if (curr == NULL) {
100 return NULL;
101 }
102
103 size_t rem_size = curr->size - needed_block_size;
104 if (rem_size >= MIN_BLOCK_SIZE) {
105
106 block_header_t *split_free = (block_header_t *)((char *)curr + needed_block_size);
107 split_free->magic_head = CANARY_HEAD;
108 split_free->is_free = 1;
109 split_free->size = rem_size;
110 split_free->req_size = 0;
111 split_free->next_free = NULL;
112 split_free->prev_free = NULL;
113 split_free->pad = 0;
114
115 block_footer_t *split_ftr = (block_footer_t *)((char *)split_free + rem_size - FOOTER_SIZE);
116 split_ftr->magic_foot = CANARY_FOOT;
117 split_ftr->pad = 0;
118 split_ftr->size = rem_size;
119
120
121 free_list_remove(curr);
122 free_list_add(split_free);
123
124 curr->size = needed_block_size;
125 } else {
126
127 free_list_remove(curr);
128 }
129
130 curr->magic_head = CANARY_HEAD;
131 curr->is_free = 0;
132 curr->req_size = size;
133
134 block_footer_t *curr_ftr = (block_footer_t *)((char *)curr + curr->size - FOOTER_SIZE);
135 curr_ftr->magic_foot = CANARY_FOOT;
136 curr_ftr->pad = 0;
137 curr_ftr->size = curr->size;
138
139 return (void *)((char *)curr + HEADER_SIZE);
140}
141
142void my_free(void *ptr) {
143 if (ptr == NULL) return;
144 if (!g_initialized) return;
145
146 char *p = (char *)ptr;
147 if (p < (char *)g_arena + HEADER_SIZE || p >= (char *)g_arena + ARENA_SIZE - FOOTER_SIZE) {
148 return;
149 }
150
151 if (((uintptr_t)p % ALIGNMENT) != 0) {
152 return;
153 }
154
155 block_header_t *hdr = (block_header_t *)(p - HEADER_SIZE);
156 if (hdr->magic_head != CANARY_HEAD || hdr->is_free != 0) {
157 return;
158 }
159
160 block_footer_t *ftr = (block_footer_t *)((char *)hdr + hdr->size - FOOTER_SIZE);
161 if (ftr->magic_foot != CANARY_FOOT || ftr->size != hdr->size) {
162 return;
163 }
164
165 hdr->is_free = 1;
166 hdr->req_size = 0;
167
168 block_header_t *merged = hdr;
169
170
171 char *right_addr = (char *)hdr + hdr->size;
172 if (right_addr < (char *)g_arena + ARENA_SIZE) {
173 block_header_t *right_hdr = (block_header_t *)right_addr;
174 if (right_hdr->magic_head == CANARY_HEAD && right_hdr->is_free == 1) {
175 free_list_remove(right_hdr);
176 merged->size += right_hdr->size;
177 }
178 }
179
180
181 char *left_ftr_addr = (char *)hdr - FOOTER_SIZE;
182 if (left_ftr_addr >= (char *)g_arena) {
183 block_footer_t *left_ftr = (block_footer_t *)left_ftr_addr;
184 if (left_ftr->magic_foot == CANARY_FOOT) {
185 block_header_t *left_hdr = (block_header_t *)((char *)hdr - left_ftr->size);
186 if ((char *)left_hdr >= (char *)g_arena &&
187 left_hdr->magic_head == CANARY_HEAD &&
188 left_hdr->is_free == 1) {
189 free_list_remove(left_hdr);
190 left_hdr->size += merged->size;
191 merged = left_hdr;
192 }
193 }
194 }
195
196 merged->magic_head = CANARY_HEAD;
197 merged->is_free = 1;
198 merged->req_size = 0;
199
200 block_footer_t *merged_ftr = (block_footer_t *)((char *)merged + merged->size - FOOTER_SIZE);
201 merged_ftr->magic_foot = CANARY_FOOT;
202 merged_ftr->pad = 0;
203 merged_ftr->size = merged->size;
204
205 free_list_add(merged);
206}
207
208void *my_calloc(size_t nmemb, size_t size) {
209 if (nmemb != 0 && size > SIZE_MAX / nmemb) {
210 return NULL;
211 }
212 size_t total = nmemb * size;
213 void *ptr = my_malloc(total);
214 if (ptr != NULL && total > 0) {
215 memset(ptr, 0, total);
216 }
217 return ptr;
218}
219
220void *my_realloc(void *ptr, size_t size) {
221 if (ptr == NULL) {
222 return my_malloc(size);
223 }
224 if (size == 0) {
225 my_free(ptr);
226 return NULL;
227 }
228
229 char *p = (char *)ptr;
230 if (p < (char *)g_arena + HEADER_SIZE || p >= (char *)g_arena + ARENA_SIZE - FOOTER_SIZE) {
231 return NULL;
232 }
233 if (((uintptr_t)p % ALIGNMENT) != 0) {
234 return NULL;
235 }
236
237 block_header_t *hdr = (block_header_t *)(p - HEADER_SIZE);
238 if (hdr->magic_head != CANARY_HEAD || hdr->is_free != 0) {
239 return NULL;
240 }
241
242 block_footer_t *ftr = (block_footer_t *)((char *)hdr + hdr->size - FOOTER_SIZE);
243 if (ftr->magic_foot != CANARY_FOOT || ftr->size != hdr->size) {
244 return NULL;
245 }
246
247 size_t curr_cap = hdr->size - HEADER_SIZE - FOOTER_SIZE;
248 if (size <= curr_cap) {
249 hdr->req_size = size;
250 return ptr;
251 }
252
253
254 size_t payload_size = align_up(size, ALIGNMENT);
255 if (payload_size < MIN_PAYLOAD) payload_size = MIN_PAYLOAD;
256 size_t needed_block_size = HEADER_SIZE + payload_size + FOOTER_SIZE;
257
258 char *right_addr = (char *)hdr + hdr->size;
259 if (right_addr < (char *)g_arena + ARENA_SIZE) {
260 block_header_t *right_hdr = (block_header_t *)right_addr;
261 if (right_hdr->magic_head == CANARY_HEAD && right_hdr->is_free == 1) {
262 size_t combined_size = hdr->size + right_hdr->size;
263 if (combined_size >= needed_block_size) {
264 free_list_remove(right_hdr);
265 size_t rem_size = combined_size - needed_block_size;
266 if (rem_size >= MIN_BLOCK_SIZE) {
267 block_header_t *split_free = (block_header_t *)((char *)hdr + needed_block_size);
268 split_free->magic_head = CANARY_HEAD;
269 split_free->is_free = 1;
270 split_free->size = rem_size;
271 split_free->req_size = 0;
272 split_free->next_free = NULL;
273 split_free->prev_free = NULL;
274 split_free->pad = 0;
275
276 block_footer_t *split_ftr = (block_footer_t *)((char *)split_free + rem_size - FOOTER_SIZE);
277 split_ftr->magic_foot = CANARY_FOOT;
278 split_ftr->pad = 0;
279 split_ftr->size = rem_size;
280
281 free_list_add(split_free);
282 hdr->size = needed_block_size;
283 } else {
284 hdr->size = combined_size;
285 }
286
287 hdr->req_size = size;
288 block_footer_t *hdr_ftr = (block_footer_t *)((char *)hdr + hdr->size - FOOTER_SIZE);
289 hdr_ftr->magic_foot = CANARY_FOOT;
290 hdr_ftr->pad = 0;
291 hdr_ftr->size = hdr->size;
292
293 return ptr;
294 }
295 }
296 }
297
298
299 void *new_ptr = my_malloc(size);
300 if (new_ptr == NULL) {
301 return NULL;
302 }
303
304 size_t copy_size = hdr->req_size < size ? hdr->req_size : size;
305 memcpy(new_ptr, ptr, copy_size);
306 my_free(ptr);
307
308 return new_ptr;
309}
310
311bool my_heap_check(void) {
312 if (!g_initialized) {
313 return true;
314 }
315
316 char *curr_addr = (char *)g_arena;
317 size_t total_size_seen = 0;
318 size_t free_blocks_physical = 0;
319 block_header_t *prev_phys_hdr = NULL;
320
321 while (curr_addr < (char *)g_arena + ARENA_SIZE) {
322 if (((uintptr_t)curr_addr % ALIGNMENT) != 0) {
323 return false;
324 }
325
326 block_header_t *hdr = (block_header_t *)curr_addr;
327 if (hdr->magic_head != CANARY_HEAD) {
328 return false;
329 }
330
331 if (hdr->is_free != 0 && hdr->is_free != 1) {
332 return false;
333 }
334
335 if (hdr->size < MIN_BLOCK_SIZE || (hdr->size % ALIGNMENT) != 0) {
336 return false;
337 }
338
339 if (curr_addr + hdr->size > (char *)g_arena + ARENA_SIZE) {
340 return false;
341 }
342
343 block_footer_t *ftr = (block_footer_t *)(curr_addr + hdr->size - FOOTER_SIZE);
344 if (ftr->magic_foot != CANARY_FOOT || ftr->size != hdr->size) {
345 return false;
346 }
347
348 if (hdr->is_free == 1) {
349 if (hdr->req_size != 0) {
350 return false;
351 }
352
353 if (prev_phys_hdr != NULL && prev_phys_hdr->is_free == 1) {
354 return false;
355 }
356 free_blocks_physical++;
357 } else {
358 size_t max_payload = hdr->size - HEADER_SIZE - FOOTER_SIZE;
359 if (hdr->req_size > max_payload) {
360 return false;
361 }
362 }
363
364 prev_phys_hdr = hdr;
365 total_size_seen += hdr->size;
366 curr_addr += hdr->size;
367 }
368
369 if (total_size_seen != ARENA_SIZE) {
370 return false;
371 }
372
373
374 size_t free_list_count = 0;
375 block_header_t *f = g_free_list;
376 while (f != NULL) {
377 char *f_addr = (char *)f;
378 if (f_addr < (char *)g_arena || f_addr >= (char *)g_arena + ARENA_SIZE) {
379 return false;
380 }
381
382 if (f->magic_head != CANARY_HEAD || f->is_free != 1) {
383 return false;
384 }
385
386 if (f->next_free != NULL) {
387 if (f->next_free->prev_free != f) {
388 return false;
389 }
390 }
391
392 free_list_count++;
393 if (free_list_count > ARENA_SIZE / MIN_BLOCK_SIZE) {
394 return false;
395 }
396
397 f = f->next_free;
398 }
399
400 if (free_list_count != free_blocks_physical) {
401 return false;
402 }
403
404 return true;
405}
406
407void my_stats(stats_t *out_stats) {
408 if (out_stats == NULL) return;
409 if (!g_initialized) my_init();
410
411 size_t bytes_in_use = 0;
412 size_t free_block_count = 0;
413 size_t largest_free_block = 0;
414
415 char *curr_addr = (char *)g_arena;
416 while (curr_addr < (char *)g_arena + ARENA_SIZE) {
417 block_header_t *hdr = (block_header_t *)curr_addr;
418 if (hdr->magic_head == CANARY_HEAD) {
419 if (hdr->is_free == 0) {
420 bytes_in_use += hdr->req_size;
421 } else {
422 free_block_count++;
423 size_t payload_cap = hdr->size - HEADER_SIZE - FOOTER_SIZE;
424 if (payload_cap > largest_free_block) {
425 largest_free_block = payload_cap;
426 }
427 }
428 curr_addr += hdr->size;
429 } else {
430 break;
431 }
432 }
433
434 out_stats->bytes_in_use = bytes_in_use;
435 out_stats->free_block_count = free_block_count;
436 out_stats->largest_free_block = largest_free_block;
437}
438
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.