aboutsummaryrefslogtreecommitdiff
path: root/libvpx/third_party/nestegg/halloc/src/halloc.c
blob: 8860d736a54a5ff1f6a58a6aeb82177310dd09b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/*
 *	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 "third_party/nestegg/halloc/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);
	}
}