CI: Various updates to buildAsterisk.sh
[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
183         if (AST_VECTOR_INIT(&mgr->string_fields, initial_vector_size)) {
184                 return -1;
185         }
186
187         while ((struct ast_string_field_mgr *) p != mgr) {
188                 AST_VECTOR_APPEND(&mgr->string_fields, p);
189                 *p++ = __ast_string_field_empty;
190         }
191
192         *pool_head = NULL;
193         mgr->embedded_pool = NULL;
194         if (add_string_pool(mgr, pool_head, needed, file, lineno, func)) {
195                 AST_VECTOR_FREE(&mgr->string_fields);
196                 return -1;
197         }
198
199         return 0;
200 }
201
202 ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
203         struct ast_string_field_pool **pool_head, size_t needed,
204         const char *file, int lineno, const char *func)
205 {
206         char *result = NULL;
207         size_t space = (*pool_head)->size - (*pool_head)->used;
208         size_t to_alloc;
209
210         /* Make room for ast_string_field_allocation and make it a multiple of that. */
211         to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
212         ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
213
214         if (__builtin_expect(to_alloc > space, 0)) {
215                 size_t new_size = (*pool_head)->size;
216
217                 while (new_size < to_alloc) {
218                         new_size *= 2;
219                 }
220
221                 if (add_string_pool(mgr, pool_head, new_size, file, lineno, func)) {
222                         return NULL;
223                 }
224         }
225
226         /* pool->base is always aligned (gcc aligned attribute). We ensure that
227          * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
228          * causing result to always be aligned as well; which in turn fixes that
229          * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
230         result = (*pool_head)->base + (*pool_head)->used;
231         (*pool_head)->used += to_alloc;
232         (*pool_head)->active += needed;
233         result += ast_alignof(ast_string_field_allocation);
234         AST_STRING_FIELD_ALLOCATION(result) = needed;
235         mgr->last_alloc = result;
236
237         return result;
238 }
239
240 int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
241         struct ast_string_field_pool **pool_head, size_t needed, const ast_string_field *ptr)
242 {
243         ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
244         size_t space = (*pool_head)->size - (*pool_head)->used;
245
246         if (*ptr != mgr->last_alloc) {
247                 return 1;
248         }
249
250         if (space < grow) {
251                 return 1;
252         }
253
254         (*pool_head)->used += grow;
255         (*pool_head)->active += grow;
256         AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
257
258         return 0;
259 }
260
261 void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
262         const ast_string_field ptr)
263 {
264         struct ast_string_field_pool *pool, *prev;
265
266         if (ptr == __ast_string_field_empty) {
267                 return;
268         }
269
270         for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
271                 if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
272                         pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
273                         if (pool->active == 0) {
274                                 if (prev) {
275                                         prev->prev = pool->prev;
276                                         ast_free(pool);
277                                 } else {
278                                         pool->used = 0;
279                                 }
280                         }
281                         break;
282                 }
283         }
284 }
285
286 void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
287         struct ast_string_field_pool **pool_head, ast_string_field *ptr,
288         const char *format, va_list ap,
289         const char *file, int lineno, const char *func)
290 {
291         size_t needed;
292         size_t available;
293         size_t space = (*pool_head)->size - (*pool_head)->used;
294         int res;
295         ssize_t grow;
296         char *target;
297         va_list ap2;
298
299         /* if the field already has space allocated, try to reuse it;
300            otherwise, try to use the empty space at the end of the current
301            pool
302         */
303         if (*ptr != __ast_string_field_empty) {
304                 target = (char *) *ptr;
305                 available = AST_STRING_FIELD_ALLOCATION(*ptr);
306                 if (*ptr == mgr->last_alloc) {
307                         available += space;
308                 }
309         } else {
310                 /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
311                  * so we don't need to re-align anything here.
312                  */
313                 target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
314                 if (space > ast_alignof(ast_string_field_allocation)) {
315                         available = space - ast_alignof(ast_string_field_allocation);
316                 } else {
317                         available = 0;
318                 }
319         }
320
321         va_copy(ap2, ap);
322         res = vsnprintf(target, available, format, ap2);
323         va_end(ap2);
324
325         if (res < 0) {
326                 /* Are we out of memory? */
327                 return;
328         }
329         if (res == 0) {
330                 __ast_string_field_release_active(*pool_head, *ptr);
331                 *ptr = __ast_string_field_empty;
332                 return;
333         }
334         needed = (size_t)res + 1; /* NUL byte */
335
336         if (needed > available) {
337                 /* the allocation could not be satisfied using the field's current allocation
338                    (if it has one), or the space available in the pool (if it does not). allocate
339                    space for it, adding a new string pool if necessary.
340                 */
341                 target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed, file, lineno, func);
342                 if (!target) {
343                         return;
344                 }
345                 vsprintf(target, format, ap);
346                 va_end(ap); /* XXX va_end without va_start? */
347                 __ast_string_field_release_active(*pool_head, *ptr);
348                 *ptr = target;
349         } else if (*ptr != target) {
350                 /* the allocation was satisfied using available space in the pool, but not
351                    using the space already allocated to the field
352                 */
353                 __ast_string_field_release_active(*pool_head, *ptr);
354                 mgr->last_alloc = *ptr = target;
355                 ast_assert(needed < (ast_string_field_allocation)-1);
356                 AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
357                 (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
358                 (*pool_head)->active += needed;
359         } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
360                 /* the allocation was satisfied by using available space in the pool *and*
361                    the field was the last allocated field from the pool, so it grew
362                 */
363                 AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
364                 (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
365                 (*pool_head)->active += grow;
366         }
367 }
368
369 void __ast_string_field_ptr_build(const char *file, int lineno, const char *func,
370         struct ast_string_field_mgr *mgr,
371         struct ast_string_field_pool **pool_head, ast_string_field *ptr, const char *format, ...)
372 {
373         va_list ap;
374
375         va_start(ap, format);
376         __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap, file, lineno, func);
377         va_end(ap);
378 }
379
380 void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size,
381         size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size,
382         const char *file, int lineno, const char *func)
383 {
384         struct ast_string_field_mgr *mgr;
385         struct ast_string_field_pool *pool;
386         struct ast_string_field_pool **pool_head;
387         size_t pool_size_needed = sizeof(*pool) + pool_size;
388         size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
389         void *allocation;
390         const char **p;
391         size_t initial_vector_size;
392
393         ast_assert(num_structs == 1);
394
395         allocation = __ast_calloc(num_structs, size_to_alloc, file, lineno, func);
396         if (!allocation) {
397                 return NULL;
398         }
399
400         mgr = allocation + field_mgr_offset;
401
402         pool = allocation + struct_size;
403         pool_head = allocation + field_mgr_pool_offset;
404         p = (const char **) pool_head + 1;
405         initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
406
407         if (AST_VECTOR_INIT(&mgr->string_fields, initial_vector_size)) {
408                 ast_free(allocation);
409                 return NULL;
410         }
411
412         while ((struct ast_string_field_mgr *) p != mgr) {
413                 AST_VECTOR_APPEND(&mgr->string_fields, p);
414                 *p++ = __ast_string_field_empty;
415         }
416
417         mgr->embedded_pool = pool;
418         *pool_head = pool;
419         pool->size = size_to_alloc - struct_size - sizeof(*pool);
420
421         return allocation;
422 }
423
424 int __ast_string_fields_cmp(struct ast_string_field_vector *left,
425         struct ast_string_field_vector *right)
426 {
427         int i;
428         int res = 0;
429
430         ast_assert(AST_VECTOR_SIZE(left) == AST_VECTOR_SIZE(right));
431
432         for (i = 0; i < AST_VECTOR_SIZE(left); i++) {
433                 if ((res = strcmp(*AST_VECTOR_GET(left, i), *AST_VECTOR_GET(right, i)))) {
434                         return res;
435                 }
436         }
437
438         return res;
439 }
440
441 int __ast_string_fields_copy(struct ast_string_field_pool *copy_pool,
442         struct ast_string_field_mgr *copy_mgr, struct ast_string_field_mgr *orig_mgr,
443         const char *file, int lineno, const char *func)
444 {
445         int i;
446         struct ast_string_field_vector *dest = &(copy_mgr->string_fields);
447         struct ast_string_field_vector *src = &(orig_mgr->string_fields);
448
449         ast_assert(AST_VECTOR_SIZE(dest) == AST_VECTOR_SIZE(src));
450
451         for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
452                 __ast_string_field_release_active(copy_pool, *AST_VECTOR_GET(dest, i));
453                 *AST_VECTOR_GET(dest, i) = __ast_string_field_empty;
454         }
455
456         for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
457                 if (__ast_string_field_ptr_set_by_fields(copy_pool, *copy_mgr, AST_VECTOR_GET(dest, i),
458                         *AST_VECTOR_GET(src, i), file, lineno, func)) {
459                         return -1;
460                 }
461         }
462
463         return 0;
464 }