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