1#include "alloc.h"
2
3#include <stdint.h>
4#include <string.h>
5
6#define CANARY_VALUE UINT64_C(0xc0ffee1234fedcba)
7#define MIN_SPLIT_PAYLOAD MY_ALIGNMENT
8
9typedef struct block_header {
10 size_t size;
11 int free;
12 struct block_header *prev;
13 struct block_header *next;
14 uint64_t canary;
15} block_header;
16
17typedef union arena_storage {
18 unsigned char bytes[MY_ARENA_SIZE];
19 max_align_t align;
20} arena_storage;
21
22static arena_storage arena;
23static block_header *head;
24static int initialized;
25
26static size_t align_up(size_t size) {
27 return (size + (MY_ALIGNMENT - 1u)) & ~(size_t)(MY_ALIGNMENT - 1u);
28}
29
30static size_t header_size(void) {
31 return align_up(sizeof(block_header));
32}
33
34static size_t footer_size(void) {
35 return align_up(sizeof(uint64_t));
36}
37
38static uint64_t *tail_canary(block_header *block) {
39 return (uint64_t *)((unsigned char *)block + header_size() + block->size);
40}
41
42static void set_canaries(block_header *block) {
43 block->canary = CANARY_VALUE;
44 *tail_canary(block) = CANARY_VALUE;
45}
46
47static void init_heap(void) {
48 if (initialized) {
49 return;
50 }
51
52 head = (block_header *)arena.bytes;
53 head->size = MY_ARENA_SIZE - header_size() - footer_size();
54 head->free = 1;
55 head->prev = NULL;
56 head->next = NULL;
57 set_canaries(head);
58 initialized = 1;
59}
60
61static void split_block(block_header *block, size_t size) {
62 const size_t overhead = header_size() + footer_size();
63
64 if (block->size < size + overhead + MIN_SPLIT_PAYLOAD) {
65 return;
66 }
67
68 block_header *new_block = (block_header *)((unsigned char *)block + overhead + size);
69 new_block->size = block->size - size - overhead;
70 new_block->free = 1;
71 new_block->prev = block;
72 new_block->next = block->next;
73 if (new_block->next != NULL) {
74 new_block->next->prev = new_block;
75 }
76
77 block->size = size;
78 block->next = new_block;
79 set_canaries(block);
80 set_canaries(new_block);
81}
82
83static void coalesce(block_header *block) {
84 const size_t overhead = header_size() + footer_size();
85
86 if (block->next != NULL && block->next->free) {
87 block_header *next = block->next;
88 block->size += overhead + next->size;
89 block->next = next->next;
90 if (block->next != NULL) {
91 block->next->prev = block;
92 }
93 set_canaries(block);
94 }
95
96 if (block->prev != NULL && block->prev->free) {
97 block_header *prev = block->prev;
98 prev->size += overhead + block->size;
99 prev->next = block->next;
100 if (prev->next != NULL) {
101 prev->next->prev = prev;
102 }
103 set_canaries(prev);
104 }
105}
106
107static block_header *ptr_to_block(void *ptr) {
108 if (ptr == NULL) {
109 return NULL;
110 }
111 return (block_header *)((unsigned char *)ptr - header_size());
112}
113
114void *my_malloc(size_t size) {
115 if (size == 0) {
116 return NULL;
117 }
118
119 init_heap();
120 size = align_up(size);
121
122 for (block_header *block = head; block != NULL; block = block->next) {
123 if (block->free && block->size >= size) {
124 split_block(block, size);
125 block->free = 0;
126 set_canaries(block);
127 return (unsigned char *)block + header_size();
128 }
129 }
130
131 return NULL;
132}
133
134void my_free(void *ptr) {
135 if (ptr == NULL) {
136 return;
137 }
138
139 block_header *block = ptr_to_block(ptr);
140 if (block->canary != CANARY_VALUE || *tail_canary(block) != CANARY_VALUE) {
141 return;
142 }
143
144 block->free = 1;
145 coalesce(block);
146}
147
148void *my_calloc(size_t count, size_t size) {
149 if (count != 0 && size > (SIZE_MAX / count)) {
150 return NULL;
151 }
152
153 size_t total = count * size;
154 void *ptr = my_malloc(total);
155 if (ptr != NULL) {
156 memset(ptr, 0, total);
157 }
158 return ptr;
159}
160
161void *my_realloc(void *ptr, size_t size) {
162 if (ptr == NULL) {
163 return my_malloc(size);
164 }
165 if (size == 0) {
166 my_free(ptr);
167 return NULL;
168 }
169
170 block_header *block = ptr_to_block(ptr);
171 if (block->canary != CANARY_VALUE || *tail_canary(block) != CANARY_VALUE) {
172 return NULL;
173 }
174
175 size_t new_size = align_up(size);
176 if (new_size <= block->size) {
177 split_block(block, new_size);
178 return ptr;
179 }
180
181 if (block->next != NULL && block->next->free) {
182 size_t combined = block->size + header_size() + footer_size() + block->next->size;
183 if (combined >= new_size) {
184 block_header *next = block->next;
185 block->size = combined;
186 block->next = next->next;
187 if (block->next != NULL) {
188 block->next->prev = block;
189 }
190 split_block(block, new_size);
191 block->free = 0;
192 set_canaries(block);
193 return ptr;
194 }
195 }
196
197 void *new_ptr = my_malloc(size);
198 if (new_ptr == NULL) {
199 return NULL;
200 }
201
202 memcpy(new_ptr, ptr, block->size < size ? block->size : size);
203 my_free(ptr);
204 return new_ptr;
205}
206
207int my_heap_check(void) {
208 init_heap();
209
210 unsigned char *arena_start = arena.bytes;
211 unsigned char *arena_end = arena.bytes + MY_ARENA_SIZE;
212 unsigned char *expected = arena_start;
213
214 for (block_header *block = head; block != NULL; block = block->next) {
215 unsigned char *block_start = (unsigned char *)block;
216 unsigned char *payload = block_start + header_size();
217 unsigned char *tail = payload + block->size;
218 unsigned char *block_end = tail + footer_size();
219
220 if (block_start != expected || block_start < arena_start || block_end > arena_end) {
221 return 0;
222 }
223 if (((uintptr_t)payload % MY_ALIGNMENT) != 0 || (block->size % MY_ALIGNMENT) != 0) {
224 return 0;
225 }
226 if (block->canary != CANARY_VALUE || *(uint64_t *)tail != CANARY_VALUE) {
227 return 0;
228 }
229 if (block->next != NULL && block->next->prev != block) {
230 return 0;
231 }
232 if (block->free && block->next != NULL && block->next->free) {
233 return 0;
234 }
235
236 expected = block_end;
237 }
238
239 return expected == arena_end;
240}
241
242my_stats_result my_stats(void) {
243 init_heap();
244
245 my_stats_result stats = {0, 0, 0};
246 for (block_header *block = head; block != NULL; block = block->next) {
247 if (block->free) {
248 stats.free_block_count++;
249 if (block->size > stats.largest_free_block) {
250 stats.largest_free_block = block->size;
251 }
252 } else {
253 stats.bytes_in_use += block->size;
254 }
255 }
256 return stats;
257}
258
259void my_heap_reset(void) {
260 initialized = 0;
261 head = NULL;
262 init_heap();
263}
264
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.