xref: /openbsd/sbin/unwind/libunbound/util/regional.c (revision d500c338)
1ae8c6e27Sflorian /*
2ae8c6e27Sflorian  * regional.c -- region based memory allocator.
3ae8c6e27Sflorian  *
4ae8c6e27Sflorian  * Copyright (c) 2001-2006, NLnet Labs. All rights reserved.
5ae8c6e27Sflorian  *
6ae8c6e27Sflorian  * Copyright (c) 2007, NLnet Labs. All rights reserved.
7ae8c6e27Sflorian  *
8ae8c6e27Sflorian  * This software is open source.
9ae8c6e27Sflorian  *
10ae8c6e27Sflorian  * Redistribution and use in source and binary forms, with or without
11ae8c6e27Sflorian  * modification, are permitted provided that the following conditions
12ae8c6e27Sflorian  * are met:
13ae8c6e27Sflorian  *
14ae8c6e27Sflorian  * Redistributions of source code must retain the above copyright notice,
15ae8c6e27Sflorian  * this list of conditions and the following disclaimer.
16ae8c6e27Sflorian  *
17ae8c6e27Sflorian  * Redistributions in binary form must reproduce the above copyright notice,
18ae8c6e27Sflorian  * this list of conditions and the following disclaimer in the documentation
19ae8c6e27Sflorian  * and/or other materials provided with the distribution.
20ae8c6e27Sflorian  *
21ae8c6e27Sflorian  * Neither the name of the NLNET LABS nor the names of its contributors may
22ae8c6e27Sflorian  * be used to endorse or promote products derived from this software without
23ae8c6e27Sflorian  * specific prior written permission.
24ae8c6e27Sflorian  *
25ae8c6e27Sflorian  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26ae8c6e27Sflorian  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27ae8c6e27Sflorian  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28ae8c6e27Sflorian  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29ae8c6e27Sflorian  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30ae8c6e27Sflorian  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
31ae8c6e27Sflorian  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32ae8c6e27Sflorian  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33ae8c6e27Sflorian  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34ae8c6e27Sflorian  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35ae8c6e27Sflorian  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36ae8c6e27Sflorian  */
37ae8c6e27Sflorian 
38ae8c6e27Sflorian /**
39ae8c6e27Sflorian  * \file
40ae8c6e27Sflorian  * Regional allocator. Allocates small portions of of larger chunks.
41ae8c6e27Sflorian  */
42ae8c6e27Sflorian 
43ae8c6e27Sflorian #include "config.h"
44ae8c6e27Sflorian #include "util/log.h"
45ae8c6e27Sflorian #include "util/regional.h"
46ae8c6e27Sflorian 
47ae8c6e27Sflorian #ifdef ALIGNMENT
48ae8c6e27Sflorian #  undef ALIGNMENT
49ae8c6e27Sflorian #endif
50ae8c6e27Sflorian /** increase size until it fits alignment of s bytes */
51ae8c6e27Sflorian #define ALIGN_UP(x, s)     (((x) + s - 1) & (~(s - 1)))
52ae8c6e27Sflorian /** what size to align on; make sure a char* fits in it. */
53ae8c6e27Sflorian #define ALIGNMENT          (sizeof(uint64_t))
54ae8c6e27Sflorian 
55ae8c6e27Sflorian /** Default reasonable size for chunks */
56ae8c6e27Sflorian #define REGIONAL_CHUNK_SIZE         8192
57ae8c6e27Sflorian #ifdef UNBOUND_ALLOC_NONREGIONAL
58ae8c6e27Sflorian /** All objects allocated outside of chunks, for debug */
59ae8c6e27Sflorian #define REGIONAL_LARGE_OBJECT_SIZE  0
60ae8c6e27Sflorian #else
61ae8c6e27Sflorian /** Default size for large objects - allocated outside of chunks. */
62ae8c6e27Sflorian #define REGIONAL_LARGE_OBJECT_SIZE  2048
63ae8c6e27Sflorian #endif
64ae8c6e27Sflorian 
65ae8c6e27Sflorian struct regional*
regional_create(void)66ae8c6e27Sflorian regional_create(void)
67ae8c6e27Sflorian {
68ae8c6e27Sflorian 	return regional_create_custom(REGIONAL_CHUNK_SIZE);
69ae8c6e27Sflorian }
70ae8c6e27Sflorian 
71ae8c6e27Sflorian /** init regional struct with first block */
72ae8c6e27Sflorian static void
regional_init(struct regional * r)73ae8c6e27Sflorian regional_init(struct regional* r)
74ae8c6e27Sflorian {
75ae8c6e27Sflorian 	size_t a = ALIGN_UP(sizeof(struct regional), ALIGNMENT);
76ae8c6e27Sflorian 	r->data = (char*)r + a;
77ae8c6e27Sflorian 	r->available = r->first_size - a;
78ae8c6e27Sflorian 	r->next = NULL;
79ae8c6e27Sflorian 	r->large_list = NULL;
80ae8c6e27Sflorian 	r->total_large = 0;
81ae8c6e27Sflorian }
82ae8c6e27Sflorian 
83853e076fSflorian /**
84853e076fSflorian  * Create a new region, with custom first block and large-object sizes.
85853e076fSflorian  * @param size: length of first block.
86853e076fSflorian  * @param large_object_size: outside of chunk allocation threshold.
87853e076fSflorian  * @return: newly allocated regional.
88853e076fSflorian  */
89853e076fSflorian static struct regional*
regional_create_custom_large_object(size_t size,size_t large_object_size)90853e076fSflorian regional_create_custom_large_object(size_t size, size_t large_object_size)
91ae8c6e27Sflorian {
92853e076fSflorian 	struct regional* r;
9357403691Sflorian 	size = ALIGN_UP(size, ALIGNMENT);
94853e076fSflorian 	r = (struct regional*)malloc(size);
95ae8c6e27Sflorian 	log_assert(sizeof(struct regional) <= size);
96ae8c6e27Sflorian 	if(!r) return NULL;
97ae8c6e27Sflorian 	r->first_size = size;
98853e076fSflorian 	r->large_object_size = large_object_size;
99ae8c6e27Sflorian 	regional_init(r);
100ae8c6e27Sflorian 	return r;
101ae8c6e27Sflorian }
102ae8c6e27Sflorian 
103853e076fSflorian struct regional*
regional_create_custom(size_t size)104853e076fSflorian regional_create_custom(size_t size)
105853e076fSflorian {
106a1a7ba80Sflorian 	if(size < sizeof(struct regional))
107a1a7ba80Sflorian 		size = sizeof(struct regional);
108853e076fSflorian 	return regional_create_custom_large_object(size,
109853e076fSflorian 		REGIONAL_LARGE_OBJECT_SIZE);
110853e076fSflorian }
111853e076fSflorian 
112853e076fSflorian struct regional*
regional_create_nochunk(size_t size)113853e076fSflorian regional_create_nochunk(size_t size)
114853e076fSflorian {
115853e076fSflorian 	return regional_create_custom_large_object(size, 0);
116853e076fSflorian }
117853e076fSflorian 
118ae8c6e27Sflorian void
regional_free_all(struct regional * r)119ae8c6e27Sflorian regional_free_all(struct regional *r)
120ae8c6e27Sflorian {
121ae8c6e27Sflorian 	char* p = r->next, *np;
122ae8c6e27Sflorian 	while(p) {
123ae8c6e27Sflorian 		np = *(char**)p;
124ae8c6e27Sflorian 		free(p);
125ae8c6e27Sflorian 		p = np;
126ae8c6e27Sflorian 	}
127ae8c6e27Sflorian 	p = r->large_list;
128ae8c6e27Sflorian 	while(p) {
129ae8c6e27Sflorian 		np = *(char**)p;
130ae8c6e27Sflorian 		free(p);
131ae8c6e27Sflorian 		p = np;
132ae8c6e27Sflorian 	}
133ae8c6e27Sflorian 	regional_init(r);
134ae8c6e27Sflorian }
135ae8c6e27Sflorian 
136ae8c6e27Sflorian void
regional_destroy(struct regional * r)137ae8c6e27Sflorian regional_destroy(struct regional *r)
138ae8c6e27Sflorian {
139ae8c6e27Sflorian 	if(!r) return;
140ae8c6e27Sflorian 	regional_free_all(r);
141ae8c6e27Sflorian 	free(r);
142ae8c6e27Sflorian }
143ae8c6e27Sflorian 
144ae8c6e27Sflorian void *
regional_alloc(struct regional * r,size_t size)145ae8c6e27Sflorian regional_alloc(struct regional *r, size_t size)
146ae8c6e27Sflorian {
14757403691Sflorian 	size_t a;
148ae8c6e27Sflorian 	void *s;
14957403691Sflorian 	if(
15057403691Sflorian #if SIZEOF_SIZE_T == 8
15157403691Sflorian 		(unsigned long long)size >= 0xffffffffffffff00ULL
15257403691Sflorian #else
15357403691Sflorian 		(unsigned)size >= (unsigned)0xffffff00UL
15457403691Sflorian #endif
15557403691Sflorian 		)
15657403691Sflorian 		return NULL; /* protect against integer overflow in
15757403691Sflorian 			malloc and ALIGN_UP */
15857403691Sflorian 	a = ALIGN_UP(size, ALIGNMENT);
159ae8c6e27Sflorian 	/* large objects */
160853e076fSflorian 	if(a > r->large_object_size) {
161ae8c6e27Sflorian 		s = malloc(ALIGNMENT + size);
162ae8c6e27Sflorian 		if(!s) return NULL;
163ae8c6e27Sflorian 		r->total_large += ALIGNMENT+size;
164ae8c6e27Sflorian 		*(char**)s = r->large_list;
165ae8c6e27Sflorian 		r->large_list = (char*)s;
166ae8c6e27Sflorian 		return (char*)s+ALIGNMENT;
167ae8c6e27Sflorian 	}
168ae8c6e27Sflorian 	/* create a new chunk */
169ae8c6e27Sflorian 	if(a > r->available) {
170ae8c6e27Sflorian 		s = malloc(REGIONAL_CHUNK_SIZE);
171ae8c6e27Sflorian 		if(!s) return NULL;
172ae8c6e27Sflorian 		*(char**)s = r->next;
173ae8c6e27Sflorian 		r->next = (char*)s;
174ae8c6e27Sflorian 		r->data = (char*)s + ALIGNMENT;
175ae8c6e27Sflorian 		r->available = REGIONAL_CHUNK_SIZE - ALIGNMENT;
176ae8c6e27Sflorian 	}
177ae8c6e27Sflorian 	/* put in this chunk */
178ae8c6e27Sflorian 	r->available -= a;
179ae8c6e27Sflorian 	s = r->data;
180ae8c6e27Sflorian 	r->data += a;
181ae8c6e27Sflorian 	return s;
182ae8c6e27Sflorian }
183ae8c6e27Sflorian 
184ae8c6e27Sflorian void *
regional_alloc_init(struct regional * r,const void * init,size_t size)185ae8c6e27Sflorian regional_alloc_init(struct regional* r, const void *init, size_t size)
186ae8c6e27Sflorian {
187ae8c6e27Sflorian 	void *s = regional_alloc(r, size);
188ae8c6e27Sflorian 	if(!s) return NULL;
189*d500c338Sflorian 	memmove(s, init, size);
190ae8c6e27Sflorian 	return s;
191ae8c6e27Sflorian }
192ae8c6e27Sflorian 
193ae8c6e27Sflorian void *
regional_alloc_zero(struct regional * r,size_t size)194ae8c6e27Sflorian regional_alloc_zero(struct regional *r, size_t size)
195ae8c6e27Sflorian {
196ae8c6e27Sflorian 	void *s = regional_alloc(r, size);
197ae8c6e27Sflorian 	if(!s) return NULL;
198ae8c6e27Sflorian 	memset(s, 0, size);
199ae8c6e27Sflorian 	return s;
200ae8c6e27Sflorian }
201ae8c6e27Sflorian 
202ae8c6e27Sflorian char *
regional_strdup(struct regional * r,const char * string)203ae8c6e27Sflorian regional_strdup(struct regional *r, const char *string)
204ae8c6e27Sflorian {
205ae8c6e27Sflorian 	return (char*)regional_alloc_init(r, string, strlen(string)+1);
206ae8c6e27Sflorian }
207ae8c6e27Sflorian 
208ae8c6e27Sflorian /**
209ae8c6e27Sflorian  * reasonably slow, but stats and get_mem are not supposed to be fast
210ae8c6e27Sflorian  * count the number of chunks in use
211ae8c6e27Sflorian  */
212ae8c6e27Sflorian static size_t
count_chunks(struct regional * r)213ae8c6e27Sflorian count_chunks(struct regional* r)
214ae8c6e27Sflorian {
215ae8c6e27Sflorian 	size_t c = 1;
216ae8c6e27Sflorian 	char* p = r->next;
217ae8c6e27Sflorian 	while(p) {
218ae8c6e27Sflorian 		c++;
219ae8c6e27Sflorian 		p = *(char**)p;
220ae8c6e27Sflorian 	}
221ae8c6e27Sflorian 	return c;
222ae8c6e27Sflorian }
223ae8c6e27Sflorian 
224ae8c6e27Sflorian /**
225ae8c6e27Sflorian  * also reasonably slow, counts the number of large objects
226ae8c6e27Sflorian  */
227ae8c6e27Sflorian static size_t
count_large(struct regional * r)228ae8c6e27Sflorian count_large(struct regional* r)
229ae8c6e27Sflorian {
230ae8c6e27Sflorian 	size_t c = 0;
231ae8c6e27Sflorian 	char* p = r->large_list;
232ae8c6e27Sflorian 	while(p) {
233ae8c6e27Sflorian 		c++;
234ae8c6e27Sflorian 		p = *(char**)p;
235ae8c6e27Sflorian 	}
236ae8c6e27Sflorian 	return c;
237ae8c6e27Sflorian }
238ae8c6e27Sflorian 
239ae8c6e27Sflorian void
regional_log_stats(struct regional * r)240ae8c6e27Sflorian regional_log_stats(struct regional *r)
241ae8c6e27Sflorian {
242ae8c6e27Sflorian 	/* some basic assertions put here (non time critical code) */
243ae8c6e27Sflorian 	log_assert(ALIGNMENT >= sizeof(char*));
244ae8c6e27Sflorian 	log_assert(REGIONAL_CHUNK_SIZE > ALIGNMENT);
245853e076fSflorian 	log_assert(REGIONAL_CHUNK_SIZE-ALIGNMENT > r->large_object_size);
246ae8c6e27Sflorian 	log_assert(REGIONAL_CHUNK_SIZE >= sizeof(struct regional));
247ae8c6e27Sflorian 	/* debug print */
248ae8c6e27Sflorian 	log_info("regional %u chunks, %u large",
249ae8c6e27Sflorian 		(unsigned)count_chunks(r), (unsigned)count_large(r));
250ae8c6e27Sflorian }
251ae8c6e27Sflorian 
252ae8c6e27Sflorian size_t
regional_get_mem(struct regional * r)253ae8c6e27Sflorian regional_get_mem(struct regional* r)
254ae8c6e27Sflorian {
255ae8c6e27Sflorian 	return r->first_size + (count_chunks(r)-1)*REGIONAL_CHUNK_SIZE
256ae8c6e27Sflorian 		+ r->total_large;
257ae8c6e27Sflorian }
258