mirror of https://github.com/asterisk/asterisk
changes/34/2534/1
commit
245c9ca5ce
@ -0,0 +1,508 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
Loading…
Reference in new issue