C++程序  |  561行  |  14.83 KB

/*
 * Similar core function to LGPL licensed talloc from Samba
 */
#define CHECK_ALLOCATION 0

#include "hieralloc.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#if CHECK_ALLOCATION
#include <set>
#endif

typedef struct hieralloc_header
{
	unsigned beginMagic;
	struct hieralloc_header * parent;
	struct hieralloc_header * nextSibling, * prevSibling;
	struct hieralloc_header * child;
	const char * name;
	unsigned size, childCount, refCount;
	int (* destructor)(void *);
	unsigned endMagic;
} hieralloc_header_t;

#define BEGIN_MAGIC() (13377331)
#define END_MAGIC(header) ((unsigned)((const hieralloc_header_t *)header + 1) % 0x10000 | 0x13370000)

static hieralloc_header_t hieralloc_global_header = {BEGIN_MAGIC(), 0, 0, 0, 0, "hieralloc_hieralloc_global_header", 0, 0 ,1, 0, 0x13370000};

#if CHECK_ALLOCATION
static std::set<void *> allocations;
#endif

#ifdef __cplusplus
extern "C" {
#endif
   
// Returns 1 if it's a valid header
static inline int check_header(const hieralloc_header_t * header)
{
   if (BEGIN_MAGIC() != header->beginMagic)
      printf("*** hieralloc check_header fail: %p ***\n", header + 1); 
	assert(BEGIN_MAGIC() == header->beginMagic);
	if (&hieralloc_global_header == header)
	{
      assert(0x13370000 == header->endMagic);
      return 1;
   }
	assert(END_MAGIC(header) == header->endMagic);
   assert(!header->nextSibling || header->nextSibling->prevSibling == header);
   assert(!header->nextSibling || header->nextSibling->parent == header->parent);
   assert(!header->prevSibling || header->prevSibling->nextSibling == header);
   assert(!header->prevSibling || header->prevSibling->parent == header->parent);
	return 1;
}

static inline hieralloc_header_t * get_header(const void *ptr)
{
	hieralloc_header_t * header = (hieralloc_header_t *)(ptr) - 1;
	check_header(header);
	return header;
}

static void check_children(hieralloc_header_t * header)
{
   check_header(header);
   unsigned childCount = 0;
   hieralloc_header_t * child = header->child;
   while (child)
   {
      childCount++;
      check_children(child);
      child = child->nextSibling;
   }
   assert(childCount == header->childCount);
}


// attach to parent and siblings
static void add_to_parent(hieralloc_header_t * parent, hieralloc_header_t * header)
{
	assert(NULL == header->parent);
	assert(NULL == header->prevSibling);
	assert(NULL == header->nextSibling);

	if (parent->child)
   {
//      hieralloc_header_t * child = parent->child;
//      while (child)
//      {
//         assert(child != header);
//         child = child->nextSibling;
//      }
		parent->child->prevSibling = header;
   }
	header->nextSibling = parent->child;
	header->prevSibling = NULL;
	header->parent = parent;
	parent->child = header;
	parent->childCount++;
   
   assert(!header->nextSibling || header->nextSibling->prevSibling == header);
   assert(!header->nextSibling || header->nextSibling->parent == header->parent);
   assert(!header->prevSibling || header->prevSibling->nextSibling == header);
   assert(!header->prevSibling || header->prevSibling->parent == header->parent);
}

// detach from parent and siblings
static void remove_from_parent(hieralloc_header_t * header)
{
   hieralloc_header_t * parent = header->parent;
	hieralloc_header_t * sibling = header->prevSibling;
   assert(!header->nextSibling || header->nextSibling->prevSibling == header);
   assert(!header->nextSibling || header->nextSibling->parent == header->parent);
   assert(!header->prevSibling || header->prevSibling->nextSibling == header);
   assert(!header->prevSibling || header->prevSibling->parent == header->parent);
	if (sibling)
	{
      if (sibling->nextSibling != header)
      {
         printf("&sibling->nextSibling=%p &header=%p \n", &sibling->nextSibling, &header);
			printf("sibling->nextSibling=%p header=%p \n", sibling->nextSibling, header);
      }
		sibling->nextSibling = header->nextSibling;
		if (header->nextSibling)
			header->nextSibling->prevSibling = sibling;
		header->prevSibling = NULL;
		header->nextSibling = NULL;
	}
	else
	{
      assert(parent->child == header);
		parent->child = header->nextSibling;
		if (header->nextSibling)
			header->nextSibling->prevSibling = NULL;
		header->nextSibling = NULL;
	}
	header->parent = NULL;
	parent->childCount--;
}

// allocate memory and attach to parent context and siblings
void * hieralloc_allocate(const void * context, unsigned size, const char * name)
{
	hieralloc_header_t * ptr = (hieralloc_header_t *)malloc(size + sizeof(hieralloc_header_t));
	assert(ptr);
	memset(ptr, 0xcd, sizeof(*ptr));
	ptr->beginMagic = BEGIN_MAGIC();
   ptr->parent = ptr->child = ptr->prevSibling = ptr->nextSibling = NULL;
	ptr->name = name;
	ptr->size = size;
	ptr->childCount = 0;
	ptr->refCount = 1;
   ptr->destructor = NULL;
	ptr->endMagic = END_MAGIC(ptr);

	hieralloc_header_t * parent = NULL;
	if (!context)
		parent = &hieralloc_global_header;
	else
		parent = get_header(context);
	add_to_parent(parent, ptr);
#if CHECK_ALLOCATION
   assert(allocations.find(ptr + 1) == allocations.end());
   allocations.insert(ptr + 1);
#endif
	return ptr + 1;
}

// (re)allocate memory and attach to parent context and siblings
void * hieralloc_reallocate(const void * context, void * ptr, unsigned size, const char * name)
{
	if (NULL == ptr)
		return hieralloc_allocate(context, size, name);
#if CHECK_ALLOCATION
   assert(allocations.find(ptr) != allocations.end());
#endif
	if (NULL == context)
		context = &hieralloc_global_header + 1;

	hieralloc_header_t * header = get_header(ptr);
	hieralloc_header_t * parent = header->parent;

	if (get_header(context) != get_header(ptr)->parent)
	{
		remove_from_parent(header);
		parent = get_header(context);
		add_to_parent(parent, header);
	}

	header = (hieralloc_header_t *)realloc(header, size + sizeof(hieralloc_header_t));
	assert(header);
	header->size = size;
	header->name = name;
	if (ptr == (header + 1))
		return ptr; // realloc didn't move allocation
   
   header->beginMagic = BEGIN_MAGIC();
	header->endMagic = END_MAGIC(header);
   if (header->nextSibling)
      header->nextSibling->prevSibling = header;
	if (header->prevSibling)
		header->prevSibling->nextSibling = header;
	else
		parent->child = header;

	hieralloc_header_t * child = header->child;
	while (child)
	{
		child->parent = header;
		child = child->nextSibling;
	}
#if CHECK_ALLOCATION
   allocations.erase(ptr);
   assert(allocations.find(header + 1) == allocations.end());
   allocations.insert(header + 1);
#endif
	return header + 1;
}

// calls destructor if set, and frees children.
// if destructor returns -1, then do nothing and return -1.
int hieralloc_free(void * ptr)
{
   if (!ptr)
		return 0;

   hieralloc_header_t * header = get_header(ptr);
   
	header->refCount--;
	if (header->refCount > 0)
		return -1;

	if (header->destructor)
		if (header->destructor(ptr))
			return -1;

   int ret = 0;
   
	//* TODO: implement reference and steal first
	hieralloc_header_t * child = header->child;
	while (child)
	{
		hieralloc_header_t * current = child;
		assert(!current->prevSibling);
      child = child->nextSibling;
		if (hieralloc_free(current + 1))
		{
			ret = -1;
			remove_from_parent(current);
			add_to_parent(header->parent, current);
         assert(0); // shouldn't happen since reference is always 1
		}
      
	}
   
	if (ret)
		return -1;

   assert(0 == header->childCount);
   assert(!header->child);
	remove_from_parent(header);
   memset(header, 0xfe, header->size + sizeof(*header));
#if CHECK_ALLOCATION
   assert(allocations.find(ptr) != allocations.end());
   allocations.erase(ptr);
   // don't free yet to force allocations to new addresses for checking double freeing
#else
   free(header); 
#endif
	return 0;
}

// not implemented from talloc_reference
void * hieralloc_reference(const void * ref_ctx, const void * ptr)
{
   return (void *)ptr;
}

// not implemented from talloc_unlink
int hieralloc_unlink(const void * ctx, void *ptr)
{
	if (!ptr)
		return -1;
	//assert(get_header(ptr)->refCount > 0);
	//get_header(ptr)->refCount--;
	return 0;
}

// moves allocation to new parent context; maintain children but update siblings
// returns ptr on success
void * hieralloc_steal(const void * new_ctx, const void * ptr)
{
	hieralloc_header_t * header = get_header(ptr);
	remove_from_parent(header);
	add_to_parent(get_header(new_ctx), header);
	return (void *)ptr;
}

// creates 0 allocation to be used as parent context
void * hieralloc_init(const char * name)
{
	return hieralloc_allocate(NULL, 0, name);
}

// returns global context
void * hieralloc_autofree_context()
{
	return &hieralloc_global_header + 1;
}

// sets destructor to be called before freeing; dctor return -1 aborts free
void hieralloc_set_destructor(const void * ptr, int (* destructor)(void *))
{
	get_header(ptr)->destructor = destructor;
}

// gets parent context of allocated memory
void * hieralloc_parent(const void * ptr)
{
	const hieralloc_header_t * header = get_header(ptr);
	return header->parent + 1;
}

// allocate and zero memory
void * _hieralloc_zero(const void * ctx, unsigned size, const char * name)
{
	void *p = hieralloc_allocate(ctx, size, name);
	if (p)
		memset(p, 0, size);
	return p;
}

// allocate and copy
char * hieralloc_strndup(const void * ctx, const char * str, unsigned len)
{
	if (!str)
		return NULL;
	
    const char *p = (const char *)memchr(str, '\0', len);
    len = (p ? p - str : len);
    char * ret = (char *)hieralloc_allocate(ctx, len + 1, str);
	if (!ret)
		return NULL;
	memcpy(ret, str, len);
	ret[len] = 0;
   get_header(ret)->name = ret;
	return ret;
}

// allocate and copy
char * hieralloc_strdup(const void * ctx, const char * str)
{
	if (!str)
		return NULL;
	return hieralloc_strndup(ctx, str, strlen(str));
}

static char * _hieralloc_strlendup_append(char * str, unsigned len,
        const char * append, unsigned appendLen)
{
	//char * ret = hieralloc_allocate(str, sizeof(char) * (len + appendLen + 1), str);
	//memcpy(ret, str, len);
	hieralloc_header_t * header = get_header(str);
	char * ret = (char *)hieralloc_reallocate(header->parent + 1, str, sizeof(char) * (len + appendLen + 1), str);
	if (!ret)
		return NULL;
	memcpy(ret + len, append, appendLen);
	ret[len + appendLen] = 0;
	get_header(ret)->name = ret;
	return ret;
}

// reallocate and append
char * hieralloc_strdup_append(char * str, const char * append)
{
	if (!str)
		return hieralloc_strdup(NULL, append);
	if (!append)
		return str;
	return _hieralloc_strlendup_append(str, strlen(str), append, strlen(append));
}

// reallocate and append
char * hieralloc_strndup_append(char * str, const char * append, unsigned len)
{
	if (!str)
		return hieralloc_strdup(NULL, append);
	if (!append)
		return str;
    const char *p = (const char *)memchr(append, '\0', len);
    len = (p ? p - append : len);
	return _hieralloc_strlendup_append(str, strlen(str), append, len);
}

// allocate and vsprintf
char * hieralloc_vasprintf(const void * ctx, const char * fmt, va_list va)
{
	va_list va2;
	va_copy(va2, va);
	char c = 0;
	int len = vsnprintf(&c, 1, fmt, va2); // count how many chars would be printed
	va_end(va2);

	assert(len >= 0); // some vsnprintf may return -1
	if (len < 0)
		return NULL;

	char * ret = (char *)hieralloc_allocate(ctx, len + 1, fmt);
	if (!ret)
		return NULL;

	va_copy(va2, va);
	vsnprintf(ret, len + 1, fmt, va2);
	va_end(va2);

	get_header(ret)->name = ret;
	return ret;
}

// allocate and sprintf
char * hieralloc_asprintf(const void * ctx, const char * fmt, ...)
{
	va_list va;
	va_start(va, fmt);
	char * ret = hieralloc_vasprintf(ctx, fmt, va);
	va_end(va);
	return ret;
}

// reallocate and append vsprintf
char * hieralloc_vasprintf_append(char * str, const char * fmt, va_list va)
{
	if (!str)
		return hieralloc_vasprintf(NULL, fmt, va);
	
   int len = strlen(str);
   va_list va2;
	va_copy(va2, va);
	char c = 0;
	int appendLen = vsnprintf(&c, 1, fmt, va2); // count how many chars would be printed
	va_end(va2);

	assert(appendLen >= 0); // some vsnprintf may return -1
	if (appendLen < 0)
		return str;
	str = (char *)hieralloc_reallocate(NULL, str, sizeof(char) * (len + appendLen + 1), str);
	if (!str)
		return NULL;

	va_copy(va2, va);
	vsnprintf(str + len, appendLen + 1, fmt, va2);
	va_end(va2);

	get_header(str)->name = str;
	return str;
}

// reallocate and append sprintf
char * hieralloc_asprintf_append(char * str, const char * fmt, ...)
{
	va_list va;
	va_start(va, fmt);
	str = hieralloc_vasprintf_append(str, fmt, va);
	va_end(va);
	return str;
}

static void _hieralloc_report(const hieralloc_header_t * header, FILE * file, unsigned tab)
{
	unsigned i = 0;
	for (i = 0; i < tab; i++)
		fputc(' ', file);
	check_header(header);
	fprintf(file, "%p: child=%d ref=%d size=%d dctor=%p name='%.256s' \n", header + 1,
	        header->childCount, header->refCount, header->size, header->destructor, header->name);
	const hieralloc_header_t * child = header->child;
	while (child)
	{
		_hieralloc_report(child, file, tab + 2);
		child = child->nextSibling;
	}

}

// report self and child allocations
void hieralloc_report(const void * ptr, FILE * file)
{
	if (NULL == ptr)
		ptr = &hieralloc_global_header + 1;
	fputs("hieralloc_report: \n", file);
	_hieralloc_report(get_header(ptr), file, 0);
}

static void _hieralloc_report_brief(const hieralloc_header_t * header, FILE * file, unsigned * data)
{
	check_header(header);
	data[0]++;
	data[1] += header->size;
	data[2] += header->childCount;
	data[3] += header->refCount;
	const hieralloc_header_t * child = header->child;
	while (child)
	{
		_hieralloc_report_brief(child, file, data);
		child = child->nextSibling;
	}
}

void hieralloc_report_brief(const void * ptr, FILE * file)
{
	if (NULL == ptr)
		ptr = &hieralloc_global_header + 1;
	unsigned data [4] = {0};
	_hieralloc_report_brief(get_header(ptr), file, data);
	fprintf(file, "hieralloc_report %p total: count=%d size=%d child=%d ref=%d \n",
		ptr, data[0], data[1], data[2], data[3]);
}

void hieralloc_report_lineage(const void * ptr, FILE * file, int tab)
{
   const hieralloc_header_t * header = get_header(ptr);
   if (header->parent)
      hieralloc_report_lineage(header->parent + 1, file, tab + 2);
   for (tab; tab >=0; tab--)
      fputc(' ', file);
   fprintf(file, "hieralloc_report_lineage %p: size=%d child=%d ref=%d name='%s' parent=%p \n",
      ptr, header->size, header->childCount, header->refCount, header->name, header->parent ? header->parent + 1 : NULL);
}

int hieralloc_find(const void * top, const void * ptr, FILE * file, int tab)
{
   int found = 0;
   if (NULL == top)
      top = &hieralloc_global_header + 1;
   if (0 == tab && file)
      fputs("hieralloc_find start \n", file);
   if (top == ptr)
   {
      if (file)
         fprintf(file, "hieralloc_found %p \n", ptr);
      found++;
   }
   const hieralloc_header_t * start = get_header(top);
   const hieralloc_header_t * child = start->child;
	while (child)
	{
		found += hieralloc_find(child + 1, ptr, file, tab + 2);
		child = child->nextSibling;
	}
   if (0 == tab && file)
      fprintf(file, "hieralloc_find end found=%d \n", found);
   return found;
}

#ifdef __cplusplus
} // extern "C"
#endif