Rev 7520 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
7520 | siemargl | 1 | /* |
2 | * Easy and fast memory allocator from |
||
3 | * https://wiki.osdev.org/Memory_Allocation |
||
4 | * Coded by Siemargl, 2018 |
||
5 | * |
||
6 | * No Garbage Collector |
||
7 | */ |
||
8 | |||
9 | #include |
||
10 | #include |
||
11 | #include |
||
12 | #include |
||
13 | #include |
||
14 | |||
15 | #define UINT_MAX (4294967295U) |
||
16 | |||
17 | #ifndef NDEBUG |
||
18 | #include |
||
7537 | siemargl | 19 | # ifdef __TINYC__ |
20 | # include |
||
21 | # define TRACE1(s, a) { char buf[400]; sprintf(buf, s, a); debug_out_str(buf); } |
||
22 | # define TRACE2(s, a, b) { char buf[400]; sprintf(buf, s, a, b); debug_out_str(buf); } |
||
23 | # else |
||
24 | # define TRACE1(s, a) printf(s, a) |
||
25 | # define TRACE2(s, a, b) printf(s, a, b) |
||
26 | # endif |
||
7520 | siemargl | 27 | #else |
28 | # define TRACE1(s, a) (void)0 |
||
29 | # define TRACE2(s, a, b) (void)0 |
||
30 | #endif |
||
31 | |||
7537 | siemargl | 32 | |
33 | |||
34 | |||
7520 | siemargl | 35 | // get address, fromwhere function was called |
36 | #define CALLEDFROM(param1) (*(int*)((char*)¶m1-4)-5) |
||
37 | |||
38 | const uint32_t c_used = 0x44455355; //'USED' |
||
39 | const uint32_t c_free = 0x45455246; //'FREE' |
||
40 | |||
41 | struct hdrfree { |
||
42 | uint32_t mark; // 'FREE' |
||
43 | size_t size; // including header |
||
44 | struct hdrfree *prev; |
||
45 | struct hdrfree *next; |
||
46 | }; |
||
47 | |||
48 | struct hdrused { |
||
49 | uint32_t mark; // 'USED' |
||
50 | size_t size; |
||
51 | }; |
||
52 | |||
53 | |||
54 | static char *__freebase = NULL; // begin of free area |
||
55 | static char *__freetop = NULL; // after last byte of free area |
||
56 | static struct hdrfree *__firstfree = NULL; // ptr to first node in dual-link list |
||
57 | |||
7537 | siemargl | 58 | static struct { |
59 | uint32_t malloc_calls; |
||
60 | uint32_t malloc_max; |
||
61 | uint32_t malloc_sum; |
||
62 | |||
63 | uint32_t sysalloc_calls; |
||
64 | uint32_t sysalloc_max; |
||
65 | uint32_t sysalloc_sum; |
||
66 | |||
67 | uint32_t crtfreeblocks; // number of free blocks, checking corruptions |
||
68 | uint32_t freeblocks_sum; |
||
69 | } wtalloc_stat; |
||
7520 | siemargl | 70 | |
7537 | siemargl | 71 | |
7520 | siemargl | 72 | void *wtmalloc(size_t sz) |
73 | { |
||
74 | struct hdrfree *fndnode, *newnode; |
||
75 | sz = (sizeof(struct hdrused) + sz + 15) & ~15; // align 16bytes |
||
76 | //TRACE1("_call alloc(%d)\n", sz); |
||
7537 | siemargl | 77 | |
78 | //statistics |
||
79 | wtalloc_stat.malloc_calls++; |
||
80 | if (sz > wtalloc_stat.malloc_max) wtalloc_stat.malloc_max = sz; |
||
81 | wtalloc_stat.malloc_sum += sz; |
||
7520 | siemargl | 82 | |
83 | // try to find free block enough size |
||
84 | fndnode = __firstfree; |
||
85 | while(fndnode) |
||
86 | { |
||
87 | #ifndef NDEBUG |
||
88 | if (fndnode->mark != c_free) |
||
89 | { |
||
90 | TRACE2("heap free block list corrupt %x EIP@%x\n", fndnode, CALLEDFROM(sz)); |
||
91 | assert(0); |
||
92 | } |
||
93 | #endif |
||
94 | if (fndnode->size >= sz) break; |
||
95 | fndnode = fndnode->next; |
||
96 | } |
||
97 | |||
98 | if (fndnode) // found free block |
||
99 | { |
||
100 | if (fndnode->size - sz > 15) // split smaller size, move free node |
||
101 | { |
||
102 | //TRACE2("alloc(%d) split (%x)\n", sz, fndnode); |
||
7537 | siemargl | 103 | wtalloc_stat.freeblocks_sum -= sz; |
7520 | siemargl | 104 | newnode = (struct hdrfree*)((char*)fndnode + sz); |
105 | newnode->mark = c_free; |
||
106 | newnode->size = fndnode->size - sz; |
||
107 | newnode->next = fndnode->next; |
||
108 | newnode->prev = fndnode->prev; |
||
109 | |||
110 | if (fndnode->next) |
||
111 | fndnode->next->prev = newnode; |
||
112 | |||
113 | //перед может быть не нода, а 1й указатель |
||
7537 | siemargl | 114 | if (fndnode->prev) |
115 | newnode->prev->next = newnode; |
||
116 | else |
||
7520 | siemargl | 117 | __firstfree = newnode; |
118 | } else // nothing to split, just exclude |
||
119 | { |
||
120 | //TRACE1("alloc(%d) remove freenode\n", sz); |
||
121 | |||
7537 | siemargl | 122 | wtalloc_stat.crtfreeblocks--; |
123 | wtalloc_stat.freeblocks_sum -= fndnode->size; |
||
7520 | siemargl | 124 | if (fndnode->next) |
125 | fndnode->next->prev = fndnode->prev; |
||
126 | //перед может быть не нода, а 1й указатель |
||
7537 | siemargl | 127 | if (fndnode->prev) |
128 | fndnode->prev->next = fndnode->next; |
||
129 | else |
||
7520 | siemargl | 130 | __firstfree = fndnode->next; |
131 | } |
||
132 | |||
133 | fndnode->mark = c_used; |
||
134 | fndnode->size = sz; |
||
135 | return (char*)fndnode + sizeof(struct hdrused); |
||
136 | } |
||
137 | |||
138 | char *ptr; |
||
139 | // free block not found, try to add @end |
||
140 | if (__freetop - __freebase < sz) // not enough memory - call system |
||
141 | { |
||
142 | if (sz > UINT_MAX - 16) return NULL; // check 32-bit heap overflow |
||
7537 | siemargl | 143 | // size_t new_heap_size = (__freetop - __freebase + sz + 4095) & ~4095; |
144 | size_t new_heap_size = (sz + sz / 5 + 4095) & ~4095; // 20% reserved |
||
7520 | siemargl | 145 | |
7537 | siemargl | 146 | //statistics |
147 | wtalloc_stat.sysalloc_calls++; |
||
148 | if (new_heap_size > wtalloc_stat.malloc_max) wtalloc_stat.sysalloc_max = new_heap_size; |
||
149 | wtalloc_stat.sysalloc_sum += new_heap_size; |
||
150 | |||
151 | |||
7520 | siemargl | 152 | //хвост сунуть в свободные, а фритоп и базу перености на новый кусок |
153 | ptr = sysmalloc(new_heap_size); // rounded 4k |
||
154 | //TRACE2("call systemalloc(%d) returned %x\n", new_heap_size, ptr); |
||
155 | if (!ptr) |
||
156 | { |
||
157 | TRACE2("sysmalloc failed trying to allocate %u bytes EIP@%x\n", sz, CALLEDFROM(sz)); |
||
158 | return NULL; |
||
159 | } |
||
160 | // add new free block in front of list |
||
161 | if (__freetop - __freebase > 15) |
||
162 | { |
||
163 | newnode = (struct hdrfree*)__freebase; |
||
164 | newnode->mark = c_free; |
||
165 | newnode->size = __freetop - __freebase; |
||
166 | newnode->next = __firstfree; |
||
167 | newnode->prev = NULL; |
||
168 | if (__firstfree) |
||
169 | __firstfree->prev = newnode; |
||
170 | __firstfree = newnode; |
||
7537 | siemargl | 171 | wtalloc_stat.crtfreeblocks++; |
172 | wtalloc_stat.freeblocks_sum += newnode->size; |
||
7520 | siemargl | 173 | //TRACE2("alloc(%d) add tail %d to freenode", sz, newnode->size); |
174 | //TRACE1(".tail [%x]\n", newnode); |
||
175 | } |
||
176 | // we don't save allocated block from system, so cant free them ltr |
||
177 | |||
178 | __freebase = ptr; |
||
179 | __freetop = __freebase + new_heap_size; |
||
180 | } |
||
181 | |||
182 | ptr = __freebase + sizeof(struct hdrused); |
||
183 | ((struct hdrused*)__freebase)->mark = c_used; |
||
184 | ((struct hdrused*)__freebase)->size = sz; |
||
185 | __freebase += sz; |
||
186 | //TRACE1("__freebase [%x]\n", __freebase); |
||
7537 | siemargl | 187 | |
188 | |||
189 | // check list availability |
||
190 | /* |
||
191 | int maxfree = 0; |
||
192 | for (fndnode = __firstfree; fndnode; fndnode = fndnode->next) |
||
193 | { |
||
194 | if (fndnode->size > maxfree) maxfree = fndnode->size; |
||
195 | } |
||
196 | |||
197 | TRACE2("alloc(%d) from freebase, maxfree = %d,", sz, maxfree); |
||
198 | TRACE1(" freelist len = %u \n", wtalloc_stat.crtfreeblocks); |
||
199 | */ |
||
7520 | siemargl | 200 | return ptr; |
201 | } |
||
202 | |||
203 | void wtfree(void *ptr) |
||
204 | { |
||
205 | if (!ptr) return; |
||
206 | |||
7537 | siemargl | 207 | //TRACE1("free() to freenode, sized %d\n", ((struct hdrused*)((char*)ptr - 8))->size); |
208 | |||
7520 | siemargl | 209 | #ifndef NDEBUG |
210 | if (((struct hdrused*)((char*)ptr - 8))->mark != c_used) |
||
211 | { |
||
212 | TRACE2("try free unallocated block ptr = %x bytes EIP@%x\n", ptr, CALLEDFROM(ptr)); |
||
213 | assert(0); |
||
214 | } |
||
215 | #endif |
||
216 | struct hdrfree *newnode = (struct hdrfree*)((char*)ptr - 8); |
||
217 | newnode->mark = c_free; |
||
218 | //size stays |
||
7537 | siemargl | 219 | newnode->next = NULL; |
220 | newnode->prev = NULL; |
||
221 | |||
222 | |||
223 | // experimental - try to merge, if adjanced from bottom is also freeblock |
||
224 | int reorganized = 0; |
||
225 | struct hdrfree *higher; |
||
226 | { |
||
227 | struct hdrfree *p1; |
||
228 | higher = NULL; |
||
229 | for (p1 = __firstfree; p1; p1 = p1->next) |
||
230 | { |
||
231 | higher = (struct hdrfree *)((char*)p1 + p1->size); |
||
232 | if (higher == newnode) break; |
||
233 | } |
||
234 | if (p1) // yes, it is |
||
235 | { |
||
236 | wtalloc_stat.freeblocks_sum += newnode->size; |
||
237 | p1->size += newnode->size; |
||
238 | // p1->prev, p1->next already OK |
||
239 | newnode->mark = 0; // for safety |
||
240 | newnode = p1; // continue optimization |
||
241 | //TRACE2("free block merged w/bottom sized %u bytes, list len %u\n", p1->size, wtalloc_stat.crtfreeblocks); |
||
242 | reorganized = 1; |
||
243 | } |
||
244 | } |
||
245 | |||
246 | |||
247 | /* removed, as very seldom succeeds */ |
||
248 | // experimental - try to merge, if adjanced from top is also freeblock |
||
249 | higher = (struct hdrfree *)((char*)newnode + newnode->size); |
||
250 | // dont work - we try to read after our memory |
||
251 | // if ((char*)higher < (char*)__freetop && // saves from reading out of our memory |
||
252 | // higher->mark == c_free) // only suspisious, must be in list |
||
253 | { |
||
254 | struct hdrfree *p1; |
||
255 | for (p1 = __firstfree; p1 && p1 != higher; p1 = p1->next); |
||
256 | if (p1) // yes, it is |
||
257 | { |
||
258 | if (newnode->next || newnode->prev) // optimized 1st stage, must remove from list and readd later |
||
259 | { |
||
260 | wtalloc_stat.crtfreeblocks--; |
||
261 | wtalloc_stat.freeblocks_sum -= newnode->size; |
||
262 | if (newnode->next) |
||
263 | newnode->next->prev = newnode->prev; |
||
264 | if (newnode->prev) |
||
265 | newnode->prev->next = newnode->next; |
||
266 | else |
||
267 | __firstfree = newnode->next; |
||
268 | } |
||
269 | wtalloc_stat.freeblocks_sum += newnode->size; |
||
270 | newnode->size += higher->size; |
||
271 | newnode->prev = higher->prev; |
||
272 | newnode->next = higher->next; |
||
273 | higher->mark = 0; // for safety |
||
274 | if (higher->next) |
||
275 | higher->next->prev = newnode; |
||
276 | if (higher->prev) |
||
277 | higher->prev->next = newnode; |
||
278 | else |
||
279 | __firstfree = newnode; |
||
280 | //TRACE1("free block merged w/top\n", 0); |
||
281 | reorganized = 1; |
||
282 | } |
||
283 | } |
||
284 | |||
285 | if (reorganized) return; // experimental reorganized do all work |
||
286 | |||
287 | //TRACE1("free block added\n", 0); |
||
288 | wtalloc_stat.crtfreeblocks++; |
||
289 | wtalloc_stat.freeblocks_sum += newnode->size; |
||
290 | |||
7520 | siemargl | 291 | newnode->next = __firstfree; |
292 | newnode->prev = NULL; |
||
293 | if (__firstfree) |
||
294 | __firstfree->prev = newnode; |
||
295 | __firstfree = newnode; |
||
296 | } |
||
297 | |||
298 | |||
299 | void *wtrealloc(void *ptr, size_t sz) |
||
300 | { |
||
301 | if (!ptr) return wtmalloc(sz); |
||
302 | |||
303 | struct hdrused* oldptr = (struct hdrused*)((char*)ptr - 8); |
||
304 | |||
305 | #ifndef NDEBUG |
||
306 | if (oldptr->mark != c_used) |
||
307 | { |
||
308 | TRACE2("try realloc unallocated block ptr = %x EIP@%x\n", ptr, CALLEDFROM(ptr)); |
||
309 | assert(0); |
||
310 | } |
||
311 | #endif |
||
312 | |||
313 | if (oldptr->size - 8 >= sz) return ptr; // enough room in this block, ex from freelist |
||
314 | |||
7537 | siemargl | 315 | /* experimental growth last block */ |
316 | int growth = (oldptr->size + sz + 15) & ~15; |
||
317 | if ((char*)oldptr + oldptr->size == __freebase && |
||
318 | __freetop - __freebase + oldptr->size >= growth ) // we at top, can grow up |
||
319 | { |
||
320 | wtalloc_stat.malloc_sum += growth - oldptr->size; |
||
321 | __freebase += growth - oldptr->size; |
||
322 | oldptr->size = growth; |
||
323 | return ptr; |
||
324 | } |
||
325 | |||
7520 | siemargl | 326 | void *newptr = wtmalloc(sz); |
327 | if (newptr) |
||
328 | { |
||
7537 | siemargl | 329 | memcpy(newptr, (char*)oldptr +8, oldptr->size -8); // why forgeting -8 dont fail test?!? |
7520 | siemargl | 330 | wtfree((char*)oldptr +8); |
331 | return newptr; |
||
332 | } |
||
333 | |||
334 | return NULL; |
||
335 | } |
||
336 | |||
337 | void* wtcalloc( size_t num, size_t size ) |
||
338 | { |
||
339 | void *newptr = wtmalloc(num * size); |
||
340 | if (newptr) |
||
341 | memset(newptr, 0, num * size); |
||
342 | return newptr; |
||
343 | } |
||
344 | |||
345 | |||
346 | |||
347 | |||
348 | int wtmalloc_freelist_check() |
||
349 | //контроль целостности списка фри OK == 1 |
||
350 | { |
||
351 | int cnt = 0; |
||
352 | struct hdrfree *ptr = __firstfree; |
||
353 | |||
354 | if(ptr && ptr->prev) |
||
355 | { |
||
356 | TRACE1("allocated memory freelist 1st block fail, ptr = %x\n", ptr); |
||
357 | return 0; |
||
358 | } |
||
359 | |||
360 | for(;ptr; ptr = ptr->next) |
||
361 | { |
||
362 | //TRACE1("(%x)", ptr); |
||
363 | |||
364 | cnt++; |
||
365 | if (ptr->mark != c_free) |
||
366 | { |
||
367 | TRACE1("allocated memory freelist check fail, ptr = %x\n", ptr); |
||
368 | return 0; |
||
369 | } |
||
370 | } |
||
7537 | siemargl | 371 | if (cnt != wtalloc_stat.crtfreeblocks) |
7520 | siemargl | 372 | { |
7537 | siemargl | 373 | TRACE2("allocated memory freelist check fail, length must be = %u but is %u\n", wtalloc_stat.crtfreeblocks, cnt); |
7520 | siemargl | 374 | return 0; |
375 | } |
||
376 | return 1; |
||
377 | } |
||
378 | |||
7537 | siemargl | 379 | void wtmalloc_freelist_print() |
380 | { |
||
381 | struct hdrfree *ptr = __firstfree; |
||
382 | for(;ptr; ptr = ptr->next) |
||
383 | { |
||
384 | TRACE2("(%x[%u])", ptr, ptr->size); |
||
385 | } |
||
386 | TRACE1("\n", 0); |
||
387 | |||
388 | } |
||
389 | |||
7520 | siemargl | 390 | int wtmalloc_poiner_check(void *ptr) |
391 | //контроль указателя - mark OK == 1 |
||
392 | { |
||
393 | if (((struct hdrused*)((char*)ptr - 8))->mark != c_used) |
||
394 | { |
||
395 | TRACE2("pointer watermark check fail ptr = %x bytes EIP@%x\n", ptr, CALLEDFROM(ptr)); |
||
396 | return 0; |
||
397 | } |
||
398 | return 1; |
||
399 | } |
||
7537 | siemargl | 400 | |
401 | void wtdump_alloc_stats() |
||
402 | { |
||
403 | TRACE1("----Watermark allocator stats:----\n", 0); |
||
404 | TRACE2("allocated %u calls, max of %u bytes\n", wtalloc_stat.malloc_calls, wtalloc_stat.malloc_max); |
||
405 | TRACE2("total %u bytes, average call %u bytes\n", wtalloc_stat.malloc_sum, wtalloc_stat.malloc_sum / wtalloc_stat.malloc_calls); |
||
406 | TRACE1("SYSTEM:\n", 0); |
||
407 | TRACE2("allocated %u calls, max of %u bytes\n", wtalloc_stat.sysalloc_calls, wtalloc_stat.sysalloc_max); |
||
408 | TRACE2("total %u bytes, average call %u bytes\n", wtalloc_stat.sysalloc_sum, wtalloc_stat.sysalloc_sum / wtalloc_stat.sysalloc_calls); |
||
409 | TRACE2("free list %u bytes, length %u chunks\n", wtalloc_stat.freeblocks_sum, wtalloc_stat.crtfreeblocks); |
||
410 | }>> |