Home | History | Annotate | Download | only in talloc
      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
     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 
     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 
     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
     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
    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
    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
    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.
    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
    277 void * hieralloc_reference(const void * ref_ctx, const void * ptr)
    278 {
    279    return (void *)ptr;
    280 }
    281 
    282 // not implemented from talloc_unlink
    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
    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
    303 void * hieralloc_init(const char * name)
    304 {
    305 	return hieralloc_allocate(NULL, 0, name);
    306 }
    307 
    308 // returns global 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
    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
    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
    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
    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
    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 
    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
    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
    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
    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
    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
    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
    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 
    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
    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 
    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 
    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 
    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 
    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