1 /*
2 * Similar core function to LGPL licensed talloc from Samba
3 */
4 #define CHECK_ALLOCATION 0
5
6 #include "hieralloc.h"
7 #include <stdlib.h>
8 #include <string.h>
9 #include <assert.h>
10
11 #if CHECK_ALLOCATION
12 #include <set>
13 #endif
14
15 typedef struct hieralloc_header
16 {
17 unsigned beginMagic;
18 struct hieralloc_header * parent;
19 struct hieralloc_header * nextSibling, * prevSibling;
20 struct hieralloc_header * child;
21 const char * name;
22 unsigned size, childCount, refCount;
23 int (* destructor)(void *);
24 unsigned endMagic;
25 } hieralloc_header_t;
26
27 #define BEGIN_MAGIC() (13377331)
28 #define END_MAGIC(header) ((unsigned)((const hieralloc_header_t *)header + 1) % 0x10000 | 0x13370000)
29
30 static hieralloc_header_t hieralloc_global_header = {BEGIN_MAGIC(), 0, 0, 0, 0, "hieralloc_hieralloc_global_header", 0, 0 ,1, 0, 0x13370000};
31
32 #if CHECK_ALLOCATION
33 static std::set<void *> allocations;
34 #endif
35
36 #ifdef __cplusplus
37 extern "C" {
38 #endif
39
40 // Returns 1 if it's a valid header
check_header(const hieralloc_header_t * header)41 static inline int check_header(const hieralloc_header_t * header)
42 {
43 if (BEGIN_MAGIC() != header->beginMagic)
44 printf("*** hieralloc check_header fail: %p ***\n", header + 1);
45 assert(BEGIN_MAGIC() == header->beginMagic);
46 if (&hieralloc_global_header == header)
47 {
48 assert(0x13370000 == header->endMagic);
49 return 1;
50 }
51 assert(END_MAGIC(header) == header->endMagic);
52 assert(!header->nextSibling || header->nextSibling->prevSibling == header);
53 assert(!header->nextSibling || header->nextSibling->parent == header->parent);
54 assert(!header->prevSibling || header->prevSibling->nextSibling == header);
55 assert(!header->prevSibling || header->prevSibling->parent == header->parent);
56 return 1;
57 }
58
get_header(const void * ptr)59 static inline hieralloc_header_t * get_header(const void *ptr)
60 {
61 hieralloc_header_t * header = (hieralloc_header_t *)(ptr) - 1;
62 check_header(header);
63 return header;
64 }
65
check_children(hieralloc_header_t * header)66 static void check_children(hieralloc_header_t * header)
67 {
68 check_header(header);
69 unsigned childCount = 0;
70 hieralloc_header_t * child = header->child;
71 while (child)
72 {
73 childCount++;
74 check_children(child);
75 child = child->nextSibling;
76 }
77 assert(childCount == header->childCount);
78 }
79
80
81 // attach to parent and siblings
add_to_parent(hieralloc_header_t * parent,hieralloc_header_t * header)82 static void add_to_parent(hieralloc_header_t * parent, hieralloc_header_t * header)
83 {
84 assert(NULL == header->parent);
85 assert(NULL == header->prevSibling);
86 assert(NULL == header->nextSibling);
87
88 if (parent->child)
89 {
90 // hieralloc_header_t * child = parent->child;
91 // while (child)
92 // {
93 // assert(child != header);
94 // child = child->nextSibling;
95 // }
96 parent->child->prevSibling = header;
97 }
98 header->nextSibling = parent->child;
99 header->prevSibling = NULL;
100 header->parent = parent;
101 parent->child = header;
102 parent->childCount++;
103
104 assert(!header->nextSibling || header->nextSibling->prevSibling == header);
105 assert(!header->nextSibling || header->nextSibling->parent == header->parent);
106 assert(!header->prevSibling || header->prevSibling->nextSibling == header);
107 assert(!header->prevSibling || header->prevSibling->parent == header->parent);
108 }
109
110 // detach from parent and siblings
remove_from_parent(hieralloc_header_t * header)111 static void remove_from_parent(hieralloc_header_t * header)
112 {
113 hieralloc_header_t * parent = header->parent;
114 hieralloc_header_t * sibling = header->prevSibling;
115 assert(!header->nextSibling || header->nextSibling->prevSibling == header);
116 assert(!header->nextSibling || header->nextSibling->parent == header->parent);
117 assert(!header->prevSibling || header->prevSibling->nextSibling == header);
118 assert(!header->prevSibling || header->prevSibling->parent == header->parent);
119 if (sibling)
120 {
121 if (sibling->nextSibling != header)
122 {
123 printf("&sibling->nextSibling=%p &header=%p \n", &sibling->nextSibling, &header);
124 printf("sibling->nextSibling=%p header=%p \n", sibling->nextSibling, header);
125 }
126 sibling->nextSibling = header->nextSibling;
127 if (header->nextSibling)
128 header->nextSibling->prevSibling = sibling;
129 header->prevSibling = NULL;
130 header->nextSibling = NULL;
131 }
132 else
133 {
134 assert(parent->child == header);
135 parent->child = header->nextSibling;
136 if (header->nextSibling)
137 header->nextSibling->prevSibling = NULL;
138 header->nextSibling = NULL;
139 }
140 header->parent = NULL;
141 parent->childCount--;
142 }
143
144 // allocate memory and attach to parent context and siblings
hieralloc_allocate(const void * context,unsigned size,const char * name)145 void * hieralloc_allocate(const void * context, unsigned size, const char * name)
146 {
147 hieralloc_header_t * ptr = (hieralloc_header_t *)malloc(size + sizeof(hieralloc_header_t));
148 assert(ptr);
149 memset(ptr, 0xcd, sizeof(*ptr));
150 ptr->beginMagic = BEGIN_MAGIC();
151 ptr->parent = ptr->child = ptr->prevSibling = ptr->nextSibling = NULL;
152 ptr->name = name;
153 ptr->size = size;
154 ptr->childCount = 0;
155 ptr->refCount = 1;
156 ptr->destructor = NULL;
157 ptr->endMagic = END_MAGIC(ptr);
158
159 hieralloc_header_t * parent = NULL;
160 if (!context)
161 parent = &hieralloc_global_header;
162 else
163 parent = get_header(context);
164 add_to_parent(parent, ptr);
165 #if CHECK_ALLOCATION
166 assert(allocations.find(ptr + 1) == allocations.end());
167 allocations.insert(ptr + 1);
168 #endif
169 return ptr + 1;
170 }
171
172 // (re)allocate memory and attach to parent context and siblings
hieralloc_reallocate(const void * context,void * ptr,unsigned size,const char * name)173 void * hieralloc_reallocate(const void * context, void * ptr, unsigned size, const char * name)
174 {
175 if (NULL == ptr)
176 return hieralloc_allocate(context, size, name);
177 #if CHECK_ALLOCATION
178 assert(allocations.find(ptr) != allocations.end());
179 #endif
180 if (NULL == context)
181 context = &hieralloc_global_header + 1;
182
183 hieralloc_header_t * header = get_header(ptr);
184 hieralloc_header_t * parent = header->parent;
185
186 if (get_header(context) != get_header(ptr)->parent)
187 {
188 remove_from_parent(header);
189 parent = get_header(context);
190 add_to_parent(parent, header);
191 }
192
193 header = (hieralloc_header_t *)realloc(header, size + sizeof(hieralloc_header_t));
194 assert(header);
195 header->size = size;
196 header->name = name;
197 if (ptr == (header + 1))
198 return ptr; // realloc didn't move allocation
199
200 header->beginMagic = BEGIN_MAGIC();
201 header->endMagic = END_MAGIC(header);
202 if (header->nextSibling)
203 header->nextSibling->prevSibling = header;
204 if (header->prevSibling)
205 header->prevSibling->nextSibling = header;
206 else
207 parent->child = header;
208
209 hieralloc_header_t * child = header->child;
210 while (child)
211 {
212 child->parent = header;
213 child = child->nextSibling;
214 }
215 #if CHECK_ALLOCATION
216 allocations.erase(ptr);
217 assert(allocations.find(header + 1) == allocations.end());
218 allocations.insert(header + 1);
219 #endif
220 return header + 1;
221 }
222
223 // calls destructor if set, and frees children.
224 // if destructor returns -1, then do nothing and return -1.
hieralloc_free(void * ptr)225 int hieralloc_free(void * ptr)
226 {
227 if (!ptr)
228 return 0;
229
230 hieralloc_header_t * header = get_header(ptr);
231
232 header->refCount--;
233 if (header->refCount > 0)
234 return -1;
235
236 if (header->destructor)
237 if (header->destructor(ptr))
238 return -1;
239
240 int ret = 0;
241
242 //* TODO: implement reference and steal first
243 hieralloc_header_t * child = header->child;
244 while (child)
245 {
246 hieralloc_header_t * current = child;
247 assert(!current->prevSibling);
248 child = child->nextSibling;
249 if (hieralloc_free(current + 1))
250 {
251 ret = -1;
252 remove_from_parent(current);
253 add_to_parent(header->parent, current);
254 assert(0); // shouldn't happen since reference is always 1
255 }
256
257 }
258
259 if (ret)
260 return -1;
261
262 assert(0 == header->childCount);
263 assert(!header->child);
264 remove_from_parent(header);
265 memset(header, 0xfe, header->size + sizeof(*header));
266 #if CHECK_ALLOCATION
267 assert(allocations.find(ptr) != allocations.end());
268 allocations.erase(ptr);
269 // don't free yet to force allocations to new addresses for checking double freeing
270 #else
271 free(header);
272 #endif
273 return 0;
274 }
275
276 // not implemented from talloc_reference
hieralloc_reference(const void * ref_ctx,const void * ptr)277 void * hieralloc_reference(const void * ref_ctx, const void * ptr)
278 {
279 return (void *)ptr;
280 }
281
282 // not implemented from talloc_unlink
hieralloc_unlink(const void * ctx,void * ptr)283 int hieralloc_unlink(const void * ctx, void *ptr)
284 {
285 if (!ptr)
286 return -1;
287 //assert(get_header(ptr)->refCount > 0);
288 //get_header(ptr)->refCount--;
289 return 0;
290 }
291
292 // moves allocation to new parent context; maintain children but update siblings
293 // returns ptr on success
hieralloc_steal(const void * new_ctx,const void * ptr)294 void * hieralloc_steal(const void * new_ctx, const void * ptr)
295 {
296 hieralloc_header_t * header = get_header(ptr);
297 remove_from_parent(header);
298 add_to_parent(get_header(new_ctx), header);
299 return (void *)ptr;
300 }
301
302 // creates 0 allocation to be used as parent context
hieralloc_init(const char * name)303 void * hieralloc_init(const char * name)
304 {
305 return hieralloc_allocate(NULL, 0, name);
306 }
307
308 // returns global context
hieralloc_autofree_context()309 void * hieralloc_autofree_context()
310 {
311 return &hieralloc_global_header + 1;
312 }
313
314 // sets destructor to be called before freeing; dctor return -1 aborts free
hieralloc_set_destructor(const void * ptr,int (* destructor)(void *))315 void hieralloc_set_destructor(const void * ptr, int (* destructor)(void *))
316 {
317 get_header(ptr)->destructor = destructor;
318 }
319
320 // gets parent context of allocated memory
hieralloc_parent(const void * ptr)321 void * hieralloc_parent(const void * ptr)
322 {
323 const hieralloc_header_t * header = get_header(ptr);
324 return header->parent + 1;
325 }
326
327 // allocate and zero memory
_hieralloc_zero(const void * ctx,unsigned size,const char * name)328 void * _hieralloc_zero(const void * ctx, unsigned size, const char * name)
329 {
330 void *p = hieralloc_allocate(ctx, size, name);
331 if (p)
332 memset(p, 0, size);
333 return p;
334 }
335
336 // allocate and copy
hieralloc_strndup(const void * ctx,const char * str,unsigned len)337 char * hieralloc_strndup(const void * ctx, const char * str, unsigned len)
338 {
339 if (!str)
340 return NULL;
341
342 const char *p = (const char *)memchr(str, '\0', len);
343 len = (p ? p - str : len);
344 char * ret = (char *)hieralloc_allocate(ctx, len + 1, str);
345 if (!ret)
346 return NULL;
347 memcpy(ret, str, len);
348 ret[len] = 0;
349 get_header(ret)->name = ret;
350 return ret;
351 }
352
353 // allocate and copy
hieralloc_strdup(const void * ctx,const char * str)354 char * hieralloc_strdup(const void * ctx, const char * str)
355 {
356 if (!str)
357 return NULL;
358 return hieralloc_strndup(ctx, str, strlen(str));
359 }
360
_hieralloc_strlendup_append(char * str,unsigned len,const char * append,unsigned appendLen)361 static char * _hieralloc_strlendup_append(char * str, unsigned len,
362 const char * append, unsigned appendLen)
363 {
364 //char * ret = hieralloc_allocate(str, sizeof(char) * (len + appendLen + 1), str);
365 //memcpy(ret, str, len);
366 hieralloc_header_t * header = get_header(str);
367 char * ret = (char *)hieralloc_reallocate(header->parent + 1, str, sizeof(char) * (len + appendLen + 1), str);
368 if (!ret)
369 return NULL;
370 memcpy(ret + len, append, appendLen);
371 ret[len + appendLen] = 0;
372 get_header(ret)->name = ret;
373 return ret;
374 }
375
376 // reallocate and append
hieralloc_strdup_append(char * str,const char * append)377 char * hieralloc_strdup_append(char * str, const char * append)
378 {
379 if (!str)
380 return hieralloc_strdup(NULL, append);
381 if (!append)
382 return str;
383 return _hieralloc_strlendup_append(str, strlen(str), append, strlen(append));
384 }
385
386 // reallocate and append
hieralloc_strndup_append(char * str,const char * append,unsigned len)387 char * hieralloc_strndup_append(char * str, const char * append, unsigned len)
388 {
389 if (!str)
390 return hieralloc_strdup(NULL, append);
391 if (!append)
392 return str;
393 const char *p = (const char *)memchr(append, '\0', len);
394 len = (p ? p - append : len);
395 return _hieralloc_strlendup_append(str, strlen(str), append, len);
396 }
397
398 // allocate and vsprintf
hieralloc_vasprintf(const void * ctx,const char * fmt,va_list va)399 char * hieralloc_vasprintf(const void * ctx, const char * fmt, va_list va)
400 {
401 va_list va2;
402 va_copy(va2, va);
403 char c = 0;
404 int len = vsnprintf(&c, 1, fmt, va2); // count how many chars would be printed
405 va_end(va2);
406
407 assert(len >= 0); // some vsnprintf may return -1
408 if (len < 0)
409 return NULL;
410
411 char * ret = (char *)hieralloc_allocate(ctx, len + 1, fmt);
412 if (!ret)
413 return NULL;
414
415 va_copy(va2, va);
416 vsnprintf(ret, len + 1, fmt, va2);
417 va_end(va2);
418
419 get_header(ret)->name = ret;
420 return ret;
421 }
422
423 // allocate and sprintf
hieralloc_asprintf(const void * ctx,const char * fmt,...)424 char * hieralloc_asprintf(const void * ctx, const char * fmt, ...)
425 {
426 va_list va;
427 va_start(va, fmt);
428 char * ret = hieralloc_vasprintf(ctx, fmt, va);
429 va_end(va);
430 return ret;
431 }
432
433 // reallocate and append vsprintf
hieralloc_vasprintf_append(char * str,const char * fmt,va_list va)434 char * hieralloc_vasprintf_append(char * str, const char * fmt, va_list va)
435 {
436 if (!str)
437 return hieralloc_vasprintf(NULL, fmt, va);
438
439 int len = strlen(str);
440 va_list va2;
441 va_copy(va2, va);
442 char c = 0;
443 int appendLen = vsnprintf(&c, 1, fmt, va2); // count how many chars would be printed
444 va_end(va2);
445
446 assert(appendLen >= 0); // some vsnprintf may return -1
447 if (appendLen < 0)
448 return str;
449 str = (char *)hieralloc_reallocate(NULL, str, sizeof(char) * (len + appendLen + 1), str);
450 if (!str)
451 return NULL;
452
453 va_copy(va2, va);
454 vsnprintf(str + len, appendLen + 1, fmt, va2);
455 va_end(va2);
456
457 get_header(str)->name = str;
458 return str;
459 }
460
461 // reallocate and append sprintf
hieralloc_asprintf_append(char * str,const char * fmt,...)462 char * hieralloc_asprintf_append(char * str, const char * fmt, ...)
463 {
464 va_list va;
465 va_start(va, fmt);
466 str = hieralloc_vasprintf_append(str, fmt, va);
467 va_end(va);
468 return str;
469 }
470
_hieralloc_report(const hieralloc_header_t * header,FILE * file,unsigned tab)471 static void _hieralloc_report(const hieralloc_header_t * header, FILE * file, unsigned tab)
472 {
473 unsigned i = 0;
474 for (i = 0; i < tab; i++)
475 fputc(' ', file);
476 check_header(header);
477 fprintf(file, "%p: child=%d ref=%d size=%d dctor=%p name='%.256s' \n", header + 1,
478 header->childCount, header->refCount, header->size, header->destructor, header->name);
479 const hieralloc_header_t * child = header->child;
480 while (child)
481 {
482 _hieralloc_report(child, file, tab + 2);
483 child = child->nextSibling;
484 }
485
486 }
487
488 // report self and child allocations
hieralloc_report(const void * ptr,FILE * file)489 void hieralloc_report(const void * ptr, FILE * file)
490 {
491 if (NULL == ptr)
492 ptr = &hieralloc_global_header + 1;
493 fputs("hieralloc_report: \n", file);
494 _hieralloc_report(get_header(ptr), file, 0);
495 }
496
_hieralloc_report_brief(const hieralloc_header_t * header,FILE * file,unsigned * data)497 static void _hieralloc_report_brief(const hieralloc_header_t * header, FILE * file, unsigned * data)
498 {
499 check_header(header);
500 data[0]++;
501 data[1] += header->size;
502 data[2] += header->childCount;
503 data[3] += header->refCount;
504 const hieralloc_header_t * child = header->child;
505 while (child)
506 {
507 _hieralloc_report_brief(child, file, data);
508 child = child->nextSibling;
509 }
510 }
511
hieralloc_report_brief(const void * ptr,FILE * file)512 void hieralloc_report_brief(const void * ptr, FILE * file)
513 {
514 if (NULL == ptr)
515 ptr = &hieralloc_global_header + 1;
516 unsigned data [4] = {0};
517 _hieralloc_report_brief(get_header(ptr), file, data);
518 fprintf(file, "hieralloc_report %p total: count=%d size=%d child=%d ref=%d \n",
519 ptr, data[0], data[1], data[2], data[3]);
520 }
521
hieralloc_report_lineage(const void * ptr,FILE * file,int tab)522 void hieralloc_report_lineage(const void * ptr, FILE * file, int tab)
523 {
524 const hieralloc_header_t * header = get_header(ptr);
525 if (header->parent)
526 hieralloc_report_lineage(header->parent + 1, file, tab + 2);
527 for (tab; tab >=0; tab--)
528 fputc(' ', file);
529 fprintf(file, "hieralloc_report_lineage %p: size=%d child=%d ref=%d name='%s' parent=%p \n",
530 ptr, header->size, header->childCount, header->refCount, header->name, header->parent ? header->parent + 1 : NULL);
531 }
532
hieralloc_find(const void * top,const void * ptr,FILE * file,int tab)533 int hieralloc_find(const void * top, const void * ptr, FILE * file, int tab)
534 {
535 int found = 0;
536 if (NULL == top)
537 top = &hieralloc_global_header + 1;
538 if (0 == tab && file)
539 fputs("hieralloc_find start \n", file);
540 if (top == ptr)
541 {
542 if (file)
543 fprintf(file, "hieralloc_found %p \n", ptr);
544 found++;
545 }
546 const hieralloc_header_t * start = get_header(top);
547 const hieralloc_header_t * child = start->child;
548 while (child)
549 {
550 found += hieralloc_find(child + 1, ptr, file, tab + 2);
551 child = child->nextSibling;
552 }
553 if (0 == tab && file)
554 fprintf(file, "hieralloc_find end found=%d \n", found);
555 return found;
556 }
557
558 #ifdef __cplusplus
559 } // extern "C"
560 #endif
561