core: Remove ABI effects of MALLOC_DEBUG.
[asterisk/asterisk.git] / main / stringfields.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2006, Digium, Inc.
5  *
6  * Kevin P. Fleming <kpfleming@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief String fields
22  *
23  * \author Kevin P. Fleming <kpfleming@digium.com>
24  */
25
26 #include "asterisk.h"
27
28 #include "asterisk/stringfields.h"
29 #include "asterisk/utils.h"
30
31 /* this is a little complex... string fields are stored with their
32    allocated size in the bytes preceding the string; even the
33    constant 'empty' string has to be this way, so the code that
34    checks to see if there is enough room for a new string doesn't
35    have to have any special case checks
36 */
37
38 static const struct {
39         ast_string_field_allocation allocation;
40         char string[1];
41 } __ast_string_field_empty_buffer;
42
43 ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
44
45 #define ALLOCATOR_OVERHEAD 48
46
47 static size_t optimal_alloc_size(size_t size)
48 {
49         unsigned int count;
50
51         size += ALLOCATOR_OVERHEAD;
52
53         for (count = 1; size; size >>= 1, count++);
54
55         return (1 << count) - ALLOCATOR_OVERHEAD;
56 }
57
58 /*! \brief add a new block to the pool.
59  * We can only allocate from the topmost pool, so the
60  * fields in *mgr reflect the size of that only.
61  */
62 static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
63         size_t size, const char *file, int lineno, const char *func)
64 {
65         struct ast_string_field_pool *pool;
66         size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
67
68         pool = __ast_calloc(1, alloc_size, file, lineno, func);
69         if (!pool) {
70                 return -1;
71         }
72
73         pool->prev = *pool_head;
74         pool->size = alloc_size - sizeof(*pool);
75         *pool_head = pool;
76         mgr->last_alloc = NULL;
77
78         return 0;
79 }
80
81 static void reset_field(const char **p)
82 {
83         *p = __ast_string_field_empty;
84 }
85
86 /*!
87  * \brief Internal cleanup function
88  * \internal
89  * \param mgr
90  * \param pool_head
91  * \param cleanup_type
92  *           0: Reset all string fields and free all pools except the last or embedded pool.
93  *              Keep the internal management structures so the structure can be reused.
94  *          -1: Reset all string fields and free all pools except the embedded pool.
95  *              Free the internal management structures.
96  * \param file
97  * \param lineno
98  * \param func
99  *
100  * \retval 0 Success
101  * \retval -1 Failure
102  */
103 int __ast_string_field_free_memory(struct ast_string_field_mgr *mgr,
104         struct ast_string_field_pool **pool_head, enum ast_stringfield_cleanup_type cleanup_type,
105         const char *file, int lineno, const char *func)
106 {
107         struct ast_string_field_pool *cur = NULL;
108         struct ast_string_field_pool *preserve = NULL;
109
110         /* reset all the fields regardless of cleanup type */
111         AST_VECTOR_CALLBACK_VOID(&mgr->string_fields, reset_field);
112
113         switch (cleanup_type) {
114         case AST_STRINGFIELD_DESTROY:
115                 AST_VECTOR_FREE(&mgr->string_fields);
116
117                 if (mgr->embedded_pool) { /* ALWAYS preserve the embedded pool if there is one */
118                         preserve = mgr->embedded_pool;
119                         preserve->used = preserve->active = 0;
120                 }
121
122                 break;
123         case AST_STRINGFIELD_RESET:
124                 /* Preserve the embedded pool if there is one, otherwise the last pool */
125                 if (mgr->embedded_pool) {
126                         preserve = mgr->embedded_pool;
127                 } else {
128                         if (*pool_head == NULL) {
129                                 ast_log(LOG_WARNING, "trying to reset empty pool\n");
130                                 return -1;
131                         }
132                         preserve = *pool_head;
133                 }
134                 preserve->used = preserve->active = 0;
135                 break;
136         default:
137                 return -1;
138         }
139
140         cur = *pool_head;
141         while (cur) {
142                 struct ast_string_field_pool *prev = cur->prev;
143
144                 if (cur != preserve) {
145                         ast_free(cur);
146                 }
147                 cur = prev;
148         }
149
150         *pool_head = preserve;
151         if (preserve) {
152                 preserve->prev = NULL;
153         }
154
155         return 0;
156 }
157
158 /*!
159  * \brief Internal initialization function
160  * \internal
161  * \param mgr
162  * \param pool_head
163  * \param needed
164  * \param file
165  * \param lineno
166  * \param func
167  *
168  * \retval 0 Success
169  * \retval -1 Failure
170  */
171 int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
172         int needed, const char *file, int lineno, const char *func)
173 {
174         const char **p = (const char **) pool_head + 1;
175         size_t initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
176
177         if (needed <= 0) {
178                 return __ast_string_field_free_memory(mgr, pool_head, needed, file, lineno, func);
179         }
180
181         mgr->last_alloc = NULL;
182         /* v-- MALLOC_DEBUG information */
183         mgr->owner_file = file;
184         mgr->owner_func = func;
185         mgr->owner_line = lineno;
186         /* ^-- MALLOC_DEBUG information */
187
188         if (AST_VECTOR_INIT(&mgr->string_fields, initial_vector_size)) {
189                 return -1;
190         }
191
192         while ((struct ast_string_field_mgr *) p != mgr) {
193                 AST_VECTOR_APPEND(&mgr->string_fields, p);
194                 *p++ = __ast_string_field_empty;
195         }
196
197         *pool_head = NULL;
198         mgr->embedded_pool = NULL;
199         if (add_string_pool(mgr, pool_head, needed, file, lineno, func)) {
200                 AST_VECTOR_FREE(&mgr->string_fields);
201                 return -1;
202         }
203
204         return 0;
205 }
206
207 ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
208         struct ast_string_field_pool **pool_head, size_t needed)
209 {
210         char *result = NULL;
211         size_t space = (*pool_head)->size - (*pool_head)->used;
212         size_t to_alloc;
213
214         /* Make room for ast_string_field_allocation and make it a multiple of that. */
215         to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
216         ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
217
218         if (__builtin_expect(to_alloc > space, 0)) {
219                 size_t new_size = (*pool_head)->size;
220
221                 while (new_size < to_alloc) {
222                         new_size *= 2;
223                 }
224
225                 if (add_string_pool(mgr, pool_head, new_size,
226                         mgr->owner_file, mgr->owner_line, mgr->owner_func)) {
227                         return NULL;
228                 }
229         }
230
231         /* pool->base is always aligned (gcc aligned attribute). We ensure that
232          * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
233          * causing result to always be aligned as well; which in turn fixes that
234          * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
235         result = (*pool_head)->base + (*pool_head)->used;
236         (*pool_head)->used += to_alloc;
237         (*pool_head)->active += needed;
238         result += ast_alignof(ast_string_field_allocation);
239         AST_STRING_FIELD_ALLOCATION(result) = needed;
240         mgr->last_alloc = result;
241
242         return result;
243 }
244
245 int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
246         struct ast_string_field_pool **pool_head, size_t needed, const ast_string_field *ptr)
247 {
248         ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
249         size_t space = (*pool_head)->size - (*pool_head)->used;
250
251         if (*ptr != mgr->last_alloc) {
252                 return 1;
253         }
254
255         if (space < grow) {
256                 return 1;
257         }
258
259         (*pool_head)->used += grow;
260         (*pool_head)->active += grow;
261         AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
262
263         return 0;
264 }
265
266 void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
267         const ast_string_field ptr)
268 {
269         struct ast_string_field_pool *pool, *prev;
270
271         if (ptr == __ast_string_field_empty) {
272                 return;
273         }
274
275         for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
276                 if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
277                         pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
278                         if (pool->active == 0) {
279                                 if (prev) {
280                                         prev->prev = pool->prev;
281                                         ast_free(pool);
282                                 } else {
283                                         pool->used = 0;
284                                 }
285                         }
286                         break;
287                 }
288         }
289 }
290
291 void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
292         struct ast_string_field_pool **pool_head, ast_string_field *ptr,
293         const char *format, va_list ap)
294 {
295         size_t needed;
296         size_t available;
297         size_t space = (*pool_head)->size - (*pool_head)->used;
298         int res;
299         ssize_t grow;
300         char *target;
301         va_list ap2;
302
303         /* if the field already has space allocated, try to reuse it;
304            otherwise, try to use the empty space at the end of the current
305            pool
306         */
307         if (*ptr != __ast_string_field_empty) {
308                 target = (char *) *ptr;
309                 available = AST_STRING_FIELD_ALLOCATION(*ptr);
310                 if (*ptr == mgr->last_alloc) {
311                         available += space;
312                 }
313         } else {
314                 /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
315                  * so we don't need to re-align anything here.
316                  */
317                 target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
318                 if (space > ast_alignof(ast_string_field_allocation)) {
319                         available = space - ast_alignof(ast_string_field_allocation);
320                 } else {
321                         available = 0;
322                 }
323         }
324
325         va_copy(ap2, ap);
326         res = vsnprintf(target, available, format, ap2);
327         va_end(ap2);
328
329         if (res < 0) {
330                 /* Are we out of memory? */
331                 return;
332         }
333         if (res == 0) {
334                 __ast_string_field_release_active(*pool_head, *ptr);
335                 *ptr = __ast_string_field_empty;
336                 return;
337         }
338         needed = (size_t)res + 1; /* NUL byte */
339
340         if (needed > available) {
341                 /* the allocation could not be satisfied using the field's current allocation
342                    (if it has one), or the space available in the pool (if it does not). allocate
343                    space for it, adding a new string pool if necessary.
344                 */
345                 if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
346                         return;
347                 }
348                 vsprintf(target, format, ap);
349                 va_end(ap); /* XXX va_end without va_start? */
350                 __ast_string_field_release_active(*pool_head, *ptr);
351                 *ptr = target;
352         } else if (*ptr != target) {
353                 /* the allocation was satisfied using available space in the pool, but not
354                    using the space already allocated to the field
355                 */
356                 __ast_string_field_release_active(*pool_head, *ptr);
357                 mgr->last_alloc = *ptr = target;
358                 ast_assert(needed < (ast_string_field_allocation)-1);
359                 AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
360                 (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
361                 (*pool_head)->active += needed;
362         } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
363                 /* the allocation was satisfied by using available space in the pool *and*
364                    the field was the last allocated field from the pool, so it grew
365                 */
366                 AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
367                 (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
368                 (*pool_head)->active += grow;
369         }
370 }
371
372 void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
373         struct ast_string_field_pool **pool_head, ast_string_field *ptr, const char *format, ...)
374 {
375         va_list ap;
376
377         va_start(ap, format);
378         __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
379         va_end(ap);
380 }
381
382 void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size,
383         size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size,
384         const char *file, int lineno, const char *func)
385 {
386         struct ast_string_field_mgr *mgr;
387         struct ast_string_field_pool *pool;
388         struct ast_string_field_pool **pool_head;
389         size_t pool_size_needed = sizeof(*pool) + pool_size;
390         size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
391         void *allocation;
392         const char **p;
393         size_t initial_vector_size;
394
395         ast_assert(num_structs == 1);
396
397         allocation = __ast_calloc(num_structs, size_to_alloc, file, lineno, func);
398         if (!allocation) {
399                 return NULL;
400         }
401
402         mgr = allocation + field_mgr_offset;
403
404         pool = allocation + struct_size;
405         pool_head = allocation + field_mgr_pool_offset;
406         p = (const char **) pool_head + 1;
407         initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
408
409         if (AST_VECTOR_INIT(&mgr->string_fields, initial_vector_size)) {
410                 ast_free(allocation);
411                 return NULL;
412         }
413
414         while ((struct ast_string_field_mgr *) p != mgr) {
415                 AST_VECTOR_APPEND(&mgr->string_fields, p);
416                 *p++ = __ast_string_field_empty;
417         }
418
419         mgr->embedded_pool = pool;
420         *pool_head = pool;
421         pool->size = size_to_alloc - struct_size - sizeof(*pool);
422         /* v-- MALLOC_DEBUG information */
423         mgr->owner_file = file;
424         mgr->owner_func = func;
425         mgr->owner_line = lineno;
426         /* ^-- MALLOC_DEBUG information */
427
428         return allocation;
429 }
430
431 int __ast_string_fields_cmp(struct ast_string_field_vector *left,
432         struct ast_string_field_vector *right)
433 {
434         int i;
435         int res = 0;
436
437         ast_assert(AST_VECTOR_SIZE(left) == AST_VECTOR_SIZE(right));
438
439         for (i = 0; i < AST_VECTOR_SIZE(left); i++) {
440                 if ((res = strcmp(*AST_VECTOR_GET(left, i), *AST_VECTOR_GET(right, i)))) {
441                         return res;
442                 }
443         }
444
445         return res;
446 }
447
448 int __ast_string_fields_copy(struct ast_string_field_pool *copy_pool,
449         struct ast_string_field_mgr *copy_mgr, struct ast_string_field_mgr *orig_mgr)
450 {
451         int i;
452         struct ast_string_field_vector *dest = &(copy_mgr->string_fields);
453         struct ast_string_field_vector *src = &(orig_mgr->string_fields);
454
455         ast_assert(AST_VECTOR_SIZE(dest) == AST_VECTOR_SIZE(src));
456
457         for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
458                 __ast_string_field_release_active(copy_pool, *AST_VECTOR_GET(dest, i));
459                 *AST_VECTOR_GET(dest, i) = __ast_string_field_empty;
460         }
461
462         for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
463                 if (ast_string_field_ptr_set_by_fields(copy_pool, *copy_mgr, AST_VECTOR_GET(dest, i),
464                         *AST_VECTOR_GET(src, i))) {
465                         return -1;
466                 }
467         }
468
469         return 0;
470 }