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 block_t *coalesce(block_t *b);
61
62static void free_list_push(block_t *b) {
63 b->free = 1;
64 b->free_prev = NULL;
65 b->free_next = free_head;
66 if (free_head) free_head->free_prev = b;
67 free_head = b;
68}
69
70static void init_heap(void) {
71 if (initialized) return;
72 heap_head = (block_t *)arena;
73 heap_head->size = ALIGN_UP(ARENA_SIZE - OVERHEAD);
74 heap_head->free = 1;
75 heap_head->prev = NULL;
76 heap_head->next = NULL;
77 heap_head->free_prev = NULL;
78 heap_head->free_next = NULL;
79 write_canaries(heap_head);
80 free_head = heap_head;
81 initialized = 1;
82}
83
84static void split_block(block_t *b, size_t need) {
85 size_t remaining = b->size - need;
86 if (remaining < OVERHEAD + MIN_PAYLOAD) return;
87
88 block_t *n = (block_t *)((unsigned char *)b + HEADER_SIZE + need + FOOTER_SIZE);
89 n->size = remaining - OVERHEAD;
90 n->free = 1;
91 n->prev = b;
92 n->next = b->next;
93 if (n->next) n->next->prev = n;
94 n->free_prev = NULL;
95 n->free_next = NULL;
96
97 b->size = need;
98 b->next = n;
99 write_canaries(b);
100 write_canaries(n);
101 free_list_push(n);
102 (void)coalesce(n);
103}
104
105static block_t *coalesce(block_t *b) {
106 if (b->next && b->next->free) {
107 block_t *n = b->next;
108 free_list_remove(n);
109 b->size += OVERHEAD + n->size;
110 b->next = n->next;
111 if (b->next) b->next->prev = b;
112 write_canaries(b);
113 }
114 if (b->prev && b->prev->free) {
115 block_t *p = b->prev;
116 free_list_remove(b);
117 p->size += OVERHEAD + b->size;
118 p->next = b->next;
119 if (p->next) p->next->prev = p;
120 write_canaries(p);
121 b = p;
122 }
123 return b;
124}
125
126void *my_malloc(size_t size) {
127 init_heap();
128 if (size == 0) return NULL;
129 size_t need = ALIGN_UP(size);
130 if (need < size) return NULL;
131
132 for (block_t *b = free_head; b; b = b->free_next) {
133 if (b->size >= need) {
134 if (!block_canaries_ok(b)) return NULL;
135 free_list_remove(b);
136 b->free = 0;
137 split_block(b, need);
138 write_canaries(b);
139 return (unsigned char *)b + HEADER_SIZE;
140 }
141 }
142 return NULL;
143}
144
145void my_free(void *ptr) {
146 if (!ptr) return;
147 init_heap();
148 block_t *b = (block_t *)((unsigned char *)ptr - HEADER_SIZE);
149 if ((unsigned char *)b < arena || (unsigned char *)b >= arena + ARENA_SIZE) return;
150 if (!block_canaries_ok(b) || b->free) return;
151 b->free = 1;
152 write_canaries(b);
153 free_list_push(b);
154 (void)coalesce(b);
155}
156
157void *my_calloc(size_t nmemb, size_t size) {
158 if (size != 0 && nmemb > (size_t)-1 / size) return NULL;
159 size_t total = nmemb * size;
160 void *p = my_malloc(total);
161 if (p) memset(p, 0, total);
162 return p;
163}
164
165void *my_realloc(void *ptr, size_t size) {
166 if (!ptr) return my_malloc(size);
167 if (size == 0) {
168 my_free(ptr);
169 return NULL;
170 }
171
172 init_heap();
173 block_t *b = (block_t *)((unsigned char *)ptr - HEADER_SIZE);
174 if (!block_canaries_ok(b) || b->free) return NULL;
175 size_t need = ALIGN_UP(size);
176 if (need < size) return NULL;
177
178 if (need <= b->size) {
179 split_block(b, need);
180 write_canaries(b);
181 return ptr;
182 }
183
184 if (b->next && b->next->free && b->size + OVERHEAD + b->next->size >= need) {
185 block_t *n = b->next;
186 free_list_remove(n);
187 b->size += OVERHEAD + n->size;
188 b->next = n->next;
189 if (b->next) b->next->prev = b;
190 write_canaries(b);
191 split_block(b, need);
192 write_canaries(b);
193 return (unsigned char *)b + HEADER_SIZE;
194 }
195
196 void *np = my_malloc(size);
197 if (!np) return NULL;
198 memcpy(np, ptr, b->size < size ? b->size : size);
199 my_free(ptr);
200 return np;
201}
202
203int my_heap_check(void) {
204 init_heap();
205 unsigned char *expected = arena;
206 block_t *prev = NULL;
207 for (block_t *b = heap_head; b; b = b->next) {
208 if ((unsigned char *)b != expected) return 0;
209 if (((uintptr_t)b & (ALIGNMENT - 1u)) != 0) return 0;
210 if (b->prev != prev) return 0;
211 if ((b->size & (ALIGNMENT - 1u)) != 0) return 0;
212 if ((unsigned char *)b + OVERHEAD + b->size > arena + ARENA_SIZE) return 0;
213 if (!block_canaries_ok(b)) return 0;
214 if (b->free && b->next && b->next->free) return 0;
215 expected = (unsigned char *)b + OVERHEAD + b->size;
216 prev = b;
217 }
218 if (expected != arena + ARENA_SIZE) return 0;
219
220 for (block_t *f = free_head; f; f = f->free_next) {
221 if (!f->free) return 0;
222 if (f->free_next && f->free_next->free_prev != f) return 0;
223 }
224 return 1;
225}
226
227my_stats_t my_stats(void) {
228 init_heap();
229 my_stats_t s = {0, 0, 0};
230 for (block_t *b = heap_head; b; b = b->next) {
231 if (b->free) {
232 s.free_block_count++;
233 if (b->size > s.largest_free_block) s.largest_free_block = b->size;
234 } else {
235 s.bytes_in_use += b->size;
236 }
237 }
238 return s;
239}
240
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.