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