123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- /*
- * Asterisk -- An open source telephony toolkit.
- *
- * Copyright (C) 2006, Digium, Inc.
- *
- * Kevin P. Fleming <kpfleming@digium.com>
- *
- * See http://www.asterisk.org for more information about
- * the Asterisk project. Please do not directly contact
- * any of the maintainers of this project for assistance;
- * the project provides a web site, mailing lists and IRC
- * channels for your use.
- *
- * This program is free software, distributed under the terms of
- * the GNU General Public License Version 2. See the LICENSE file
- * at the top of the source tree.
- */
- /*! \file
- *
- * \brief String fields
- *
- * \author Kevin P. Fleming <kpfleming@digium.com>
- */
- #include "asterisk.h"
- ASTERISK_REGISTER_FILE()
- #include "asterisk/stringfields.h"
- #include "asterisk/utils.h"
- /* this is a little complex... string fields are stored with their
- allocated size in the bytes preceding the string; even the
- constant 'empty' string has to be this way, so the code that
- checks to see if there is enough room for a new string doesn't
- have to have any special case checks
- */
- static const struct {
- ast_string_field_allocation allocation;
- char string[1];
- } __ast_string_field_empty_buffer;
- ast_string_field __ast_string_field_empty = __ast_string_field_empty_buffer.string;
- #define ALLOCATOR_OVERHEAD 48
- static size_t optimal_alloc_size(size_t size)
- {
- unsigned int count;
- size += ALLOCATOR_OVERHEAD;
- for (count = 1; size; size >>= 1, count++);
- return (1 << count) - ALLOCATOR_OVERHEAD;
- }
- static void *calloc_wrapper(unsigned int num_structs, size_t struct_size,
- const char *file, int lineno, const char *func)
- {
- #if defined(__AST_DEBUG_MALLOC)
- return __ast_calloc(num_structs, struct_size, file, lineno, func);
- #else
- return ast_calloc(num_structs, struct_size);
- #endif
- }
- /*! \brief add a new block to the pool.
- * We can only allocate from the topmost pool, so the
- * fields in *mgr reflect the size of that only.
- */
- static int add_string_pool(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
- size_t size, const char *file, int lineno, const char *func)
- {
- struct ast_string_field_pool *pool;
- size_t alloc_size = optimal_alloc_size(sizeof(*pool) + size);
- if (!(pool = calloc_wrapper(1, alloc_size, file, lineno, func))) {
- return -1;
- }
- pool->prev = *pool_head;
- pool->size = alloc_size - sizeof(*pool);
- *pool_head = pool;
- mgr->last_alloc = NULL;
- return 0;
- }
- static void reset_field(const char **p)
- {
- *p = __ast_string_field_empty;
- }
- /*!
- * \brief Internal cleanup function
- * \internal
- * \param mgr
- * \param pool_head
- * \param cleanup_type
- * 0: Reset all string fields and free all pools except the last or embedded pool.
- * Keep the internal management structures so the structure can be reused.
- * -1: Reset all string fields and free all pools except the embedded pool.
- * Free the internal management structures.
- * \param file
- * \param lineno
- * \param func
- *
- * \retval 0 Success
- * \retval -1 Failure
- */
- int __ast_string_field_free_memory(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, enum ast_stringfield_cleanup_type cleanup_type,
- const char *file, int lineno, const char *func)
- {
- struct ast_string_field_pool *cur = NULL;
- struct ast_string_field_pool *preserve = NULL;
- if (!mgr->header) {
- return -1;
- }
- /* reset all the fields regardless of cleanup type */
- AST_VECTOR_CALLBACK_VOID(&mgr->header->string_fields, reset_field);
- switch (cleanup_type) {
- case AST_STRINGFIELD_DESTROY:
- AST_VECTOR_FREE(&mgr->header->string_fields);
- if (mgr->header->embedded_pool) { /* ALWAYS preserve the embedded pool if there is one */
- preserve = mgr->header->embedded_pool;
- preserve->used = preserve->active = 0;
- }
- ast_free(mgr->header);
- mgr->header = NULL;
- break;
- case AST_STRINGFIELD_RESET:
- /* Preserve the embedded pool if there is one, otherwise the last pool */
- if (mgr->header->embedded_pool) {
- preserve = mgr->header->embedded_pool;
- } else {
- if (*pool_head == NULL) {
- ast_log(LOG_WARNING, "trying to reset empty pool\n");
- return -1;
- }
- preserve = *pool_head;
- }
- preserve->used = preserve->active = 0;
- break;
- default:
- return -1;
- }
- cur = *pool_head;
- while (cur) {
- struct ast_string_field_pool *prev = cur->prev;
- if (cur != preserve) {
- ast_free(cur);
- }
- cur = prev;
- }
- *pool_head = preserve;
- if (preserve) {
- preserve->prev = NULL;
- }
- return 0;
- }
- /*!
- * \brief Internal initialization function
- * \internal
- * \param mgr
- * \param pool_head
- * \param needed
- * \param file
- * \param lineno
- * \param func
- *
- * \retval 0 Success
- * \retval -1 Failure
- */
- int __ast_string_field_init(struct ast_string_field_mgr *mgr, struct ast_string_field_pool **pool_head,
- int needed, const char *file, int lineno, const char *func)
- {
- const char **p = (const char **) pool_head + 1;
- size_t initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
- if (needed <= 0) {
- return __ast_string_field_free_memory(mgr, pool_head, needed, file, lineno, func);
- }
- mgr->last_alloc = NULL;
- #if defined(__AST_DEBUG_MALLOC)
- mgr->owner_file = file;
- mgr->owner_func = func;
- mgr->owner_line = lineno;
- #endif
- if (!(mgr->header = calloc_wrapper(1, sizeof(*mgr->header), file, lineno, func))) {
- return -1;
- }
- if (AST_VECTOR_INIT(&mgr->header->string_fields, initial_vector_size)) {
- ast_free(mgr->header);
- mgr->header = NULL;
- return -1;
- }
- while ((struct ast_string_field_mgr *) p != mgr) {
- AST_VECTOR_APPEND(&mgr->header->string_fields, p);
- *p++ = __ast_string_field_empty;
- }
- *pool_head = NULL;
- mgr->header->embedded_pool = NULL;
- if (add_string_pool(mgr, pool_head, needed, file, lineno, func)) {
- AST_VECTOR_FREE(&mgr->header->string_fields);
- ast_free(mgr->header);
- mgr->header = NULL;
- return -1;
- }
- return 0;
- }
- ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, size_t needed)
- {
- char *result = NULL;
- size_t space = (*pool_head)->size - (*pool_head)->used;
- size_t to_alloc;
- /* Make room for ast_string_field_allocation and make it a multiple of that. */
- to_alloc = ast_make_room_for(needed, ast_string_field_allocation);
- ast_assert(to_alloc % ast_alignof(ast_string_field_allocation) == 0);
- if (__builtin_expect(to_alloc > space, 0)) {
- size_t new_size = (*pool_head)->size;
- while (new_size < to_alloc) {
- new_size *= 2;
- }
- #if defined(__AST_DEBUG_MALLOC)
- if (add_string_pool(mgr, pool_head, new_size, mgr->owner_file, mgr->owner_line, mgr->owner_func))
- return NULL;
- #else
- if (add_string_pool(mgr, pool_head, new_size, __FILE__, __LINE__, __FUNCTION__))
- return NULL;
- #endif
- }
- /* pool->base is always aligned (gcc aligned attribute). We ensure that
- * to_alloc is also a multiple of ast_alignof(ast_string_field_allocation)
- * causing result to always be aligned as well; which in turn fixes that
- * AST_STRING_FIELD_ALLOCATION(result) is aligned. */
- result = (*pool_head)->base + (*pool_head)->used;
- (*pool_head)->used += to_alloc;
- (*pool_head)->active += needed;
- result += ast_alignof(ast_string_field_allocation);
- AST_STRING_FIELD_ALLOCATION(result) = needed;
- mgr->last_alloc = result;
- return result;
- }
- int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, size_t needed, const ast_string_field *ptr)
- {
- ssize_t grow = needed - AST_STRING_FIELD_ALLOCATION(*ptr);
- size_t space = (*pool_head)->size - (*pool_head)->used;
- if (*ptr != mgr->last_alloc) {
- return 1;
- }
- if (space < grow) {
- return 1;
- }
- (*pool_head)->used += grow;
- (*pool_head)->active += grow;
- AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
- return 0;
- }
- void __ast_string_field_release_active(struct ast_string_field_pool *pool_head,
- const ast_string_field ptr)
- {
- struct ast_string_field_pool *pool, *prev;
- if (ptr == __ast_string_field_empty) {
- return;
- }
- for (pool = pool_head, prev = NULL; pool; prev = pool, pool = pool->prev) {
- if ((ptr >= pool->base) && (ptr <= (pool->base + pool->size))) {
- pool->active -= AST_STRING_FIELD_ALLOCATION(ptr);
- if (pool->active == 0) {
- if (prev) {
- prev->prev = pool->prev;
- ast_free(pool);
- } else {
- pool->used = 0;
- }
- }
- break;
- }
- }
- }
- void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, ast_string_field *ptr,
- const char *format, va_list ap)
- {
- size_t needed;
- size_t available;
- size_t space = (*pool_head)->size - (*pool_head)->used;
- int res;
- ssize_t grow;
- char *target;
- va_list ap2;
- /* if the field already has space allocated, try to reuse it;
- otherwise, try to use the empty space at the end of the current
- pool
- */
- if (*ptr != __ast_string_field_empty) {
- target = (char *) *ptr;
- available = AST_STRING_FIELD_ALLOCATION(*ptr);
- if (*ptr == mgr->last_alloc) {
- available += space;
- }
- } else {
- /* pool->used is always a multiple of ast_alignof(ast_string_field_allocation)
- * so we don't need to re-align anything here.
- */
- target = (*pool_head)->base + (*pool_head)->used + ast_alignof(ast_string_field_allocation);
- if (space > ast_alignof(ast_string_field_allocation)) {
- available = space - ast_alignof(ast_string_field_allocation);
- } else {
- available = 0;
- }
- }
- va_copy(ap2, ap);
- res = vsnprintf(target, available, format, ap2);
- va_end(ap2);
- if (res < 0) {
- /* Are we out of memory? */
- return;
- }
- if (res == 0) {
- __ast_string_field_release_active(*pool_head, *ptr);
- *ptr = __ast_string_field_empty;
- return;
- }
- needed = (size_t)res + 1; /* NUL byte */
- if (needed > available) {
- /* the allocation could not be satisfied using the field's current allocation
- (if it has one), or the space available in the pool (if it does not). allocate
- space for it, adding a new string pool if necessary.
- */
- if (!(target = (char *) __ast_string_field_alloc_space(mgr, pool_head, needed))) {
- return;
- }
- vsprintf(target, format, ap);
- va_end(ap); /* XXX va_end without va_start? */
- __ast_string_field_release_active(*pool_head, *ptr);
- *ptr = target;
- } else if (*ptr != target) {
- /* the allocation was satisfied using available space in the pool, but not
- using the space already allocated to the field
- */
- __ast_string_field_release_active(*pool_head, *ptr);
- mgr->last_alloc = *ptr = target;
- ast_assert(needed < (ast_string_field_allocation)-1);
- AST_STRING_FIELD_ALLOCATION(target) = (ast_string_field_allocation)needed;
- (*pool_head)->used += ast_make_room_for(needed, ast_string_field_allocation);
- (*pool_head)->active += needed;
- } else if ((grow = (needed - AST_STRING_FIELD_ALLOCATION(*ptr))) > 0) {
- /* the allocation was satisfied by using available space in the pool *and*
- the field was the last allocated field from the pool, so it grew
- */
- AST_STRING_FIELD_ALLOCATION(*ptr) += grow;
- (*pool_head)->used += ast_align_for(grow, ast_string_field_allocation);
- (*pool_head)->active += grow;
- }
- }
- void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr,
- struct ast_string_field_pool **pool_head, ast_string_field *ptr, const char *format, ...)
- {
- va_list ap;
- va_start(ap, format);
- __ast_string_field_ptr_build_va(mgr, pool_head, ptr, format, ap);
- va_end(ap);
- }
- void *__ast_calloc_with_stringfields(unsigned int num_structs, size_t struct_size,
- size_t field_mgr_offset, size_t field_mgr_pool_offset, size_t pool_size, const char *file,
- int lineno, const char *func)
- {
- struct ast_string_field_mgr *mgr;
- struct ast_string_field_pool *pool;
- struct ast_string_field_pool **pool_head;
- size_t pool_size_needed = sizeof(*pool) + pool_size;
- size_t size_to_alloc = optimal_alloc_size(struct_size + pool_size_needed);
- void *allocation;
- const char **p;
- size_t initial_vector_size;
- ast_assert(num_structs == 1);
- if (!(allocation = calloc_wrapper(num_structs, size_to_alloc, file, lineno, func))) {
- return NULL;
- }
- mgr = allocation + field_mgr_offset;
- /*
- * The header is calloced in __ast_string_field_init so it also gets calloced here
- * so __ast_string_fields_free_memory can always just free mgr->header.
- */
- mgr->header = calloc_wrapper(1, sizeof(*mgr->header), file, lineno, func);
- if (!mgr->header) {
- ast_free(allocation);
- return NULL;
- }
- pool = allocation + struct_size;
- pool_head = allocation + field_mgr_pool_offset;
- p = (const char **) pool_head + 1;
- initial_vector_size = ((size_t) (((char *)mgr) - ((char *)p))) / sizeof(*p);
- if (AST_VECTOR_INIT(&mgr->header->string_fields, initial_vector_size)) {
- ast_free(mgr->header);
- ast_free(allocation);
- return NULL;
- }
- while ((struct ast_string_field_mgr *) p != mgr) {
- AST_VECTOR_APPEND(&mgr->header->string_fields, p);
- *p++ = __ast_string_field_empty;
- }
- mgr->header->embedded_pool = pool;
- *pool_head = pool;
- pool->size = size_to_alloc - struct_size - sizeof(*pool);
- #if defined(__AST_DEBUG_MALLOC)
- mgr->owner_file = file;
- mgr->owner_func = func;
- mgr->owner_line = lineno;
- #endif
- return allocation;
- }
- int __ast_string_fields_cmp(struct ast_string_field_vector *left,
- struct ast_string_field_vector *right)
- {
- int i;
- int res = 0;
- ast_assert(AST_VECTOR_SIZE(left) == AST_VECTOR_SIZE(right));
- for (i = 0; i < AST_VECTOR_SIZE(left); i++) {
- if ((res = strcmp(*AST_VECTOR_GET(left, i), *AST_VECTOR_GET(right, i)))) {
- return res;
- }
- }
- return res;
- }
- int __ast_string_fields_copy(struct ast_string_field_pool *copy_pool,
- struct ast_string_field_mgr *copy_mgr, struct ast_string_field_mgr *orig_mgr)
- {
- int i;
- struct ast_string_field_vector *dest = &(copy_mgr->header->string_fields);
- struct ast_string_field_vector *src = &(orig_mgr->header->string_fields);
- ast_assert(AST_VECTOR_SIZE(dest) == AST_VECTOR_SIZE(src));
- for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
- __ast_string_field_release_active(copy_pool, *AST_VECTOR_GET(dest, i));
- *AST_VECTOR_GET(dest, i) = __ast_string_field_empty;
- }
- for (i = 0; i < AST_VECTOR_SIZE(dest); i++) {
- if (ast_string_field_ptr_set_by_fields(copy_pool, *copy_mgr, AST_VECTOR_GET(dest, i),
- *AST_VECTOR_GET(src, i))) {
- return -1;
- }
- }
- return 0;
- }
|