xref: /openbsd-src/usr.sbin/unbound/util/regional.c (revision 437d28603d0290bfd9d9c531721c9139b03d4aa6)
1933707f3Ssthen /*
2933707f3Ssthen  * regional.c -- region based memory allocator.
3933707f3Ssthen  *
4933707f3Ssthen  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5933707f3Ssthen  *
6933707f3Ssthen  * Copyright (c) 2007, NLnet Labs. All rights reserved.
7933707f3Ssthen  *
8933707f3Ssthen  * This software is open source.
9933707f3Ssthen  *
10933707f3Ssthen  * Redistribution and use in source and binary forms, with or without
11933707f3Ssthen  * modification, are permitted provided that the following conditions
12933707f3Ssthen  * are met:
13933707f3Ssthen  *
14933707f3Ssthen  * Redistributions of source code must retain the above copyright notice,
15933707f3Ssthen  * this list of conditions and the following disclaimer.
16933707f3Ssthen  *
17933707f3Ssthen  * Redistributions in binary form must reproduce the above copyright notice,
18933707f3Ssthen  * this list of conditions and the following disclaimer in the documentation
19933707f3Ssthen  * and/or other materials provided with the distribution.
20933707f3Ssthen  *
21933707f3Ssthen  * Neither the name of the NLNET LABS nor the names of its contributors may
22933707f3Ssthen  * be used to endorse or promote products derived from this software without
23933707f3Ssthen  * specific prior written permission.
24933707f3Ssthen  *
25933707f3Ssthen  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
265d76a658Ssthen  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
275d76a658Ssthen  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
285d76a658Ssthen  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
295d76a658Ssthen  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
305d76a658Ssthen  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
315d76a658Ssthen  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
325d76a658Ssthen  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
335d76a658Ssthen  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
345d76a658Ssthen  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
355d76a658Ssthen  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36933707f3Ssthen  */
37933707f3Ssthen 
38933707f3Ssthen /**
39933707f3Ssthen  * \file
40933707f3Ssthen  * Regional allocator. Allocates small portions of of larger chunks.
41933707f3Ssthen  */
42933707f3Ssthen 
43933707f3Ssthen #include "config.h"
44933707f3Ssthen #include "util/log.h"
45933707f3Ssthen #include "util/regional.h"
46933707f3Ssthen 
47933707f3Ssthen #ifdef ALIGNMENT
48933707f3Ssthen #  undef ALIGNMENT
49933707f3Ssthen #endif
50933707f3Ssthen /** increase size until it fits alignment of s bytes */
51933707f3Ssthen #define ALIGN_UP(x, s)     (((x) + s - 1) & (~(s - 1)))
52933707f3Ssthen /** what size to align on; make sure a char* fits in it. */
53933707f3Ssthen #define ALIGNMENT          (sizeof(uint64_t))
54933707f3Ssthen 
55933707f3Ssthen /** Default reasonable size for chunks */
56933707f3Ssthen #define REGIONAL_CHUNK_SIZE         8192
57933707f3Ssthen #ifdef UNBOUND_ALLOC_NONREGIONAL
58933707f3Ssthen /** All objects allocated outside of chunks, for debug */
59933707f3Ssthen #define REGIONAL_LARGE_OBJECT_SIZE  0
60933707f3Ssthen #else
61933707f3Ssthen /** Default size for large objects - allocated outside of chunks. */
62933707f3Ssthen #define REGIONAL_LARGE_OBJECT_SIZE  2048
63933707f3Ssthen #endif
64933707f3Ssthen 
65933707f3Ssthen struct regional*
regional_create(void)66933707f3Ssthen regional_create(void)
67933707f3Ssthen {
68933707f3Ssthen 	return regional_create_custom(REGIONAL_CHUNK_SIZE);
69933707f3Ssthen }
70933707f3Ssthen 
71933707f3Ssthen /** init regional struct with first block */
72933707f3Ssthen static void
regional_init(struct regional * r)73933707f3Ssthen regional_init(struct regional* r)
74933707f3Ssthen {
75933707f3Ssthen 	size_t a = ALIGN_UP(sizeof(struct regional), ALIGNMENT);
76933707f3Ssthen 	r->data = (char*)r + a;
77933707f3Ssthen 	r->available = r->first_size - a;
78933707f3Ssthen 	r->next = NULL;
79933707f3Ssthen 	r->large_list = NULL;
80933707f3Ssthen 	r->total_large = 0;
81933707f3Ssthen }
82933707f3Ssthen 
8372f58708Ssthen /**
8472f58708Ssthen  * Create a new region, with custom first block and large-object sizes.
8572f58708Ssthen  * @param size: length of first block.
8672f58708Ssthen  * @param large_object_size: outside of chunk allocation threshold.
8772f58708Ssthen  * @return: newly allocated regional.
8872f58708Ssthen  */
8972f58708Ssthen static struct regional*
regional_create_custom_large_object(size_t size,size_t large_object_size)9072f58708Ssthen regional_create_custom_large_object(size_t size, size_t large_object_size)
91933707f3Ssthen {
9272f58708Ssthen 	struct regional* r;
93b0dfc31bSsthen 	size = ALIGN_UP(size, ALIGNMENT);
9472f58708Ssthen 	r = (struct regional*)malloc(size);
95933707f3Ssthen 	log_assert(sizeof(struct regional) <= size);
96933707f3Ssthen 	if(!r) return NULL;
97933707f3Ssthen 	r->first_size = size;
9872f58708Ssthen 	r->large_object_size = large_object_size;
99933707f3Ssthen 	regional_init(r);
100933707f3Ssthen 	return r;
101933707f3Ssthen }
102933707f3Ssthen 
10372f58708Ssthen struct regional*
regional_create_custom(size_t size)10472f58708Ssthen regional_create_custom(size_t size)
10572f58708Ssthen {
10683152a15Ssthen 	if(size < sizeof(struct regional))
10783152a15Ssthen 		size = sizeof(struct regional);
10872f58708Ssthen 	return regional_create_custom_large_object(size,
10972f58708Ssthen 		REGIONAL_LARGE_OBJECT_SIZE);
11072f58708Ssthen }
11172f58708Ssthen 
11272f58708Ssthen struct regional*
regional_create_nochunk(size_t size)11372f58708Ssthen regional_create_nochunk(size_t size)
11472f58708Ssthen {
11572f58708Ssthen 	return regional_create_custom_large_object(size, 0);
11672f58708Ssthen }
11772f58708Ssthen 
118933707f3Ssthen void
regional_free_all(struct regional * r)119933707f3Ssthen regional_free_all(struct regional *r)
120933707f3Ssthen {
121933707f3Ssthen 	char* p = r->next, *np;
122933707f3Ssthen 	while(p) {
123933707f3Ssthen 		np = *(char**)p;
124933707f3Ssthen 		free(p);
125933707f3Ssthen 		p = np;
126933707f3Ssthen 	}
127933707f3Ssthen 	p = r->large_list;
128933707f3Ssthen 	while(p) {
129933707f3Ssthen 		np = *(char**)p;
130933707f3Ssthen 		free(p);
131933707f3Ssthen 		p = np;
132933707f3Ssthen 	}
133933707f3Ssthen 	regional_init(r);
134933707f3Ssthen }
135933707f3Ssthen 
136933707f3Ssthen void
regional_destroy(struct regional * r)137933707f3Ssthen regional_destroy(struct regional *r)
138933707f3Ssthen {
139933707f3Ssthen 	if(!r) return;
140933707f3Ssthen 	regional_free_all(r);
141933707f3Ssthen 	free(r);
142933707f3Ssthen }
143933707f3Ssthen 
144933707f3Ssthen void *
regional_alloc(struct regional * r,size_t size)145933707f3Ssthen regional_alloc(struct regional *r, size_t size)
146933707f3Ssthen {
147b0dfc31bSsthen 	size_t a;
148933707f3Ssthen 	void *s;
149b0dfc31bSsthen 	if(
150b0dfc31bSsthen #if SIZEOF_SIZE_T == 8
151b0dfc31bSsthen 		(unsigned long long)size >= 0xffffffffffffff00ULL
152b0dfc31bSsthen #else
153b0dfc31bSsthen 		(unsigned)size >= (unsigned)0xffffff00UL
154b0dfc31bSsthen #endif
155b0dfc31bSsthen 		)
156b0dfc31bSsthen 		return NULL; /* protect against integer overflow in
157b0dfc31bSsthen 			malloc and ALIGN_UP */
158b0dfc31bSsthen 	a = ALIGN_UP(size, ALIGNMENT);
159933707f3Ssthen 	/* large objects */
16072f58708Ssthen 	if(a > r->large_object_size) {
161933707f3Ssthen 		s = malloc(ALIGNMENT + size);
162933707f3Ssthen 		if(!s) return NULL;
163933707f3Ssthen 		r->total_large += ALIGNMENT+size;
164933707f3Ssthen 		*(char**)s = r->large_list;
165933707f3Ssthen 		r->large_list = (char*)s;
166933707f3Ssthen 		return (char*)s+ALIGNMENT;
167933707f3Ssthen 	}
168933707f3Ssthen 	/* create a new chunk */
169933707f3Ssthen 	if(a > r->available) {
170933707f3Ssthen 		s = malloc(REGIONAL_CHUNK_SIZE);
171933707f3Ssthen 		if(!s) return NULL;
172933707f3Ssthen 		*(char**)s = r->next;
173933707f3Ssthen 		r->next = (char*)s;
174933707f3Ssthen 		r->data = (char*)s + ALIGNMENT;
175933707f3Ssthen 		r->available = REGIONAL_CHUNK_SIZE - ALIGNMENT;
176933707f3Ssthen 	}
177933707f3Ssthen 	/* put in this chunk */
178933707f3Ssthen 	r->available -= a;
179933707f3Ssthen 	s = r->data;
180933707f3Ssthen 	r->data += a;
181933707f3Ssthen 	return s;
182933707f3Ssthen }
183933707f3Ssthen 
184933707f3Ssthen void *
regional_alloc_init(struct regional * r,const void * init,size_t size)185933707f3Ssthen regional_alloc_init(struct regional* r, const void *init, size_t size)
186933707f3Ssthen {
187933707f3Ssthen 	void *s = regional_alloc(r, size);
188933707f3Ssthen 	if(!s) return NULL;
189*437d2860Ssthen 	memmove(s, init, size);
190933707f3Ssthen 	return s;
191933707f3Ssthen }
192933707f3Ssthen 
193933707f3Ssthen void *
regional_alloc_zero(struct regional * r,size_t size)194933707f3Ssthen regional_alloc_zero(struct regional *r, size_t size)
195933707f3Ssthen {
196933707f3Ssthen 	void *s = regional_alloc(r, size);
197933707f3Ssthen 	if(!s) return NULL;
198933707f3Ssthen 	memset(s, 0, size);
199933707f3Ssthen 	return s;
200933707f3Ssthen }
201933707f3Ssthen 
202933707f3Ssthen char *
regional_strdup(struct regional * r,const char * string)203933707f3Ssthen regional_strdup(struct regional *r, const char *string)
204933707f3Ssthen {
205933707f3Ssthen 	return (char*)regional_alloc_init(r, string, strlen(string)+1);
206933707f3Ssthen }
207933707f3Ssthen 
208933707f3Ssthen /**
209933707f3Ssthen  * reasonably slow, but stats and get_mem are not supposed to be fast
210933707f3Ssthen  * count the number of chunks in use
211933707f3Ssthen  */
212933707f3Ssthen static size_t
count_chunks(struct regional * r)213933707f3Ssthen count_chunks(struct regional* r)
214933707f3Ssthen {
215933707f3Ssthen 	size_t c = 1;
216933707f3Ssthen 	char* p = r->next;
217933707f3Ssthen 	while(p) {
218933707f3Ssthen 		c++;
219933707f3Ssthen 		p = *(char**)p;
220933707f3Ssthen 	}
221933707f3Ssthen 	return c;
222933707f3Ssthen }
223933707f3Ssthen 
224933707f3Ssthen /**
225933707f3Ssthen  * also reasonably slow, counts the number of large objects
226933707f3Ssthen  */
227933707f3Ssthen static size_t
count_large(struct regional * r)228933707f3Ssthen count_large(struct regional* r)
229933707f3Ssthen {
230933707f3Ssthen 	size_t c = 0;
231933707f3Ssthen 	char* p = r->large_list;
232933707f3Ssthen 	while(p) {
233933707f3Ssthen 		c++;
234933707f3Ssthen 		p = *(char**)p;
235933707f3Ssthen 	}
236933707f3Ssthen 	return c;
237933707f3Ssthen }
238933707f3Ssthen 
239933707f3Ssthen void
regional_log_stats(struct regional * r)240933707f3Ssthen regional_log_stats(struct regional *r)
241933707f3Ssthen {
242933707f3Ssthen 	/* some basic assertions put here (non time critical code) */
243933707f3Ssthen 	log_assert(ALIGNMENT >= sizeof(char*));
244933707f3Ssthen 	log_assert(REGIONAL_CHUNK_SIZE > ALIGNMENT);
24572f58708Ssthen 	log_assert(REGIONAL_CHUNK_SIZE-ALIGNMENT > r->large_object_size);
246933707f3Ssthen 	log_assert(REGIONAL_CHUNK_SIZE >= sizeof(struct regional));
247933707f3Ssthen 	/* debug print */
248933707f3Ssthen 	log_info("regional %u chunks, %u large",
249933707f3Ssthen 		(unsigned)count_chunks(r), (unsigned)count_large(r));
250933707f3Ssthen }
251933707f3Ssthen 
252933707f3Ssthen size_t
regional_get_mem(struct regional * r)253933707f3Ssthen regional_get_mem(struct regional* r)
254933707f3Ssthen {
255933707f3Ssthen 	return r->first_size + (count_chunks(r)-1)*REGIONAL_CHUNK_SIZE
256933707f3Ssthen 		+ r->total_large;
257933707f3Ssthen }
258