| /* | 
 |  *	Copyright (c) 2004i-2010 Alex Pankratov. All rights reserved. | 
 |  * | 
 |  *	Hierarchical memory allocator, 1.2.1 | 
 |  *	http://swapped.cc/halloc | 
 |  */ | 
 |  | 
 | /* | 
 |  *	The program is distributed under terms of BSD license.  | 
 |  *	You can obtain the copy of the license by visiting: | 
 |  *	 | 
 |  *	http://www.opensource.org/licenses/bsd-license.php | 
 |  */ | 
 |  | 
 | #include <stdlib.h>  /* realloc */ | 
 | #include <string.h>  /* memset & co */ | 
 |  | 
 | #include "../halloc.h" | 
 | #include "align.h" | 
 | #include "hlist.h" | 
 |  | 
 | /* | 
 |  *	block control header | 
 |  */ | 
 | typedef struct hblock | 
 | { | 
 | #ifndef NDEBUG | 
 | #define HH_MAGIC    0x20040518L | 
 | 	long          magic; | 
 | #endif | 
 | 	hlist_item_t  siblings; /* 2 pointers */ | 
 | 	hlist_head_t  children; /* 1 pointer  */ | 
 | 	max_align_t   data[1];  /* not allocated, see below */ | 
 | 	 | 
 | } hblock_t; | 
 |  | 
 | #define sizeof_hblock offsetof(hblock_t, data) | 
 |  | 
 | /* | 
 |  * | 
 |  */ | 
 | realloc_t halloc_allocator = NULL; | 
 |  | 
 | #define allocator halloc_allocator | 
 |  | 
 | /* | 
 |  *	static methods | 
 |  */ | 
 | static void _set_allocator(void); | 
 | static void * _realloc(void * ptr, size_t n); | 
 |  | 
 | static int  _relate(hblock_t * b, hblock_t * p); | 
 | static void _free_children(hblock_t * p); | 
 |  | 
 | /* | 
 |  *	Core API | 
 |  */ | 
 | void * halloc(void * ptr, size_t len) | 
 | { | 
 | 	hblock_t * p; | 
 |  | 
 | 	/* set up default allocator */ | 
 | 	if (! allocator) | 
 | 	{ | 
 | 		_set_allocator(); | 
 | 		assert(allocator); | 
 | 	} | 
 |  | 
 | 	/* calloc */ | 
 | 	if (! ptr) | 
 | 	{ | 
 | 		if (! len) | 
 | 			return NULL; | 
 |  | 
 | 		p = allocator(0, len + sizeof_hblock); | 
 | 		if (! p) | 
 | 			return NULL; | 
 | #ifndef NDEBUG | 
 | 		p->magic = HH_MAGIC; | 
 | #endif | 
 | 		hlist_init(&p->children); | 
 | 		hlist_init_item(&p->siblings); | 
 |  | 
 | 		return p->data; | 
 | 	} | 
 |  | 
 | 	p = structof(ptr, hblock_t, data); | 
 | 	assert(p->magic == HH_MAGIC); | 
 |  | 
 | 	/* realloc */ | 
 | 	if (len) | 
 | 	{ | 
 | 		p = allocator(p, len + sizeof_hblock); | 
 | 		if (! p) | 
 | 			return NULL; | 
 |  | 
 | 		hlist_relink(&p->siblings); | 
 | 		hlist_relink_head(&p->children); | 
 | 		 | 
 | 		return p->data; | 
 | 	} | 
 |  | 
 | 	/* free */ | 
 | 	_free_children(p); | 
 | 	hlist_del(&p->siblings); | 
 | 	allocator(p, 0); | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | void hattach(void * block, void * parent) | 
 | { | 
 | 	hblock_t * b, * p; | 
 | 	 | 
 | 	if (! block) | 
 | 	{ | 
 | 		assert(! parent); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	/* detach */ | 
 | 	b = structof(block, hblock_t, data); | 
 | 	assert(b->magic == HH_MAGIC); | 
 |  | 
 | 	hlist_del(&b->siblings); | 
 |  | 
 | 	if (! parent) | 
 | 		return; | 
 |  | 
 | 	/* attach */ | 
 | 	p = structof(parent, hblock_t, data); | 
 | 	assert(p->magic == HH_MAGIC); | 
 | 	 | 
 | 	/* sanity checks */ | 
 | 	assert(b != p);          /* trivial */ | 
 | 	assert(! _relate(p, b)); /* heavy ! */ | 
 |  | 
 | 	hlist_add(&p->children, &b->siblings); | 
 | } | 
 |  | 
 | /* | 
 |  *	malloc/free api | 
 |  */ | 
 | void * h_malloc(size_t len) | 
 | { | 
 | 	return halloc(0, len); | 
 | } | 
 |  | 
 | void * h_calloc(size_t n, size_t len) | 
 | { | 
 | 	void * ptr = halloc(0, len*=n); | 
 | 	return ptr ? memset(ptr, 0, len) : NULL; | 
 | } | 
 |  | 
 | void * h_realloc(void * ptr, size_t len) | 
 | { | 
 | 	return halloc(ptr, len); | 
 | } | 
 |  | 
 | void   h_free(void * ptr) | 
 | { | 
 | 	halloc(ptr, 0); | 
 | } | 
 |  | 
 | char * h_strdup(const char * str) | 
 | { | 
 | 	size_t len = strlen(str); | 
 | 	char * ptr = halloc(0, len + 1); | 
 | 	return ptr ? (ptr[len] = 0, memcpy(ptr, str, len)) : NULL; | 
 | } | 
 |  | 
 | /* | 
 |  *	static stuff | 
 |  */ | 
 | static void _set_allocator(void) | 
 | { | 
 | 	void * p; | 
 | 	assert(! allocator); | 
 | 	 | 
 | 	/* | 
 | 	 *	the purpose of the test below is to check the behaviour | 
 | 	 *	of realloc(ptr, 0), which is defined in the standard | 
 | 	 *	as an implementation-specific. if it returns zero, | 
 | 	 *	then it's equivalent to free(). it can however return | 
 | 	 *	non-zero, in which case it cannot be used for freeing | 
 | 	 *	memory blocks and we'll need to supply our own version | 
 | 	 * | 
 | 	 *	Thanks to Stan Tobias for pointing this tricky part out. | 
 | 	 */ | 
 | 	allocator = realloc; | 
 | 	if (! (p = malloc(1))) | 
 | 		/* hmm */ | 
 | 		return; | 
 | 		 | 
 | 	if ((p = realloc(p, 0))) | 
 | 	{ | 
 | 		/* realloc cannot be used as free() */ | 
 | 		allocator = _realloc; | 
 | 		free(p); | 
 | 	} | 
 | } | 
 |  | 
 | static void * _realloc(void * ptr, size_t n) | 
 | { | 
 | 	/* | 
 | 	 *	free'ing realloc() | 
 | 	 */ | 
 | 	if (n) | 
 | 		return realloc(ptr, n); | 
 | 	free(ptr); | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int _relate(hblock_t * b, hblock_t * p) | 
 | { | 
 | 	hlist_item_t * i; | 
 |  | 
 | 	if (!b || !p) | 
 | 		return 0; | 
 |  | 
 | 	/*  | 
 | 	 *  since there is no 'parent' pointer, which would've allowed | 
 | 	 *  O(log(n)) upward traversal, the check must use O(n) downward  | 
 | 	 *  iteration of the entire hierarchy; and this can be VERY SLOW | 
 | 	 */ | 
 | 	hlist_for_each(i, &p->children) | 
 | 	{ | 
 | 		hblock_t * q = structof(i, hblock_t, siblings); | 
 | 		if (q == b || _relate(b, q)) | 
 | 			return 1; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 |  | 
 | static void _free_children(hblock_t * p) | 
 | { | 
 | 	hlist_item_t * i, * tmp; | 
 | 	 | 
 | #ifndef NDEBUG | 
 | 	/* | 
 | 	 *	this catches loops in hierarchy with almost zero  | 
 | 	 *	overhead (compared to _relate() running time) | 
 | 	 */ | 
 | 	assert(p && p->magic == HH_MAGIC); | 
 | 	p->magic = 0;  | 
 | #endif | 
 | 	hlist_for_each_safe(i, tmp, &p->children) | 
 | 	{ | 
 | 		hblock_t * q = structof(i, hblock_t, siblings); | 
 | 		_free_children(q); | 
 | 		allocator(q, 0); | 
 | 	} | 
 | } | 
 |  |