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