diff --git a/include/asterisk/stringfields.h b/include/asterisk/stringfields.h index 65f1b53f99f4f99af4acf2cd6df9ef263ee2f6f4..b0cda6902673d6458c70e19148a50423b0867b0d 100644 --- a/include/asterisk/stringfields.h +++ b/include/asterisk/stringfields.h @@ -135,10 +135,28 @@ struct ast_string_field_pool { pool, so the numbers here reflect just that. */ struct ast_string_field_mgr { - size_t size; /*!< the total size of the current pool */ - size_t used; /*!< the space used in the current pool */ + size_t size; /*!< the total size of the current pool */ + size_t used; /*!< the space used in the current pool */ + ast_string_field last_alloc; /*!< the last field allocated */ }; +/*! + \internal + \brief Attempt to 'grow' an already allocated field to a larger size + \param mgr Pointer to the pool manager structure + \param needed Amount of space needed for this field + \param ptr Pointer to a field within the structure + \return 0 on success, non-zero on failure + + This function will attempt to increase the amount of space allocated to + an existing field to the amount requested; this is only possible if the + field was the last field allocated from the current storage pool and + the pool has enough space available. If so, the additional space will be + allocated to this field and the field's address will not be changed. +*/ +int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, size_t needed, + const ast_string_field *ptr); + /*! \internal \brief Allocate space for a field @@ -152,7 +170,7 @@ struct ast_string_field_mgr { an additional pool will be allocated. */ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr, - struct ast_string_field_pool **pool_head, size_t needed); + struct ast_string_field_pool **pool_head, size_t needed); /*! \internal @@ -164,8 +182,8 @@ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr \return nothing */ void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr, - struct ast_string_field_pool **pool_head, - const ast_string_field *ptr, const char *format, ...); + struct ast_string_field_pool **pool_head, + const ast_string_field *ptr, const char *format, ...); /*! \internal @@ -179,8 +197,8 @@ void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr, \return nothing */ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr, - struct ast_string_field_pool **pool_head, - const ast_string_field *ptr, const char *format, va_list a1, va_list a2); + struct ast_string_field_pool **pool_head, + const ast_string_field *ptr, const char *format, va_list a1, va_list a2); /*! \brief Declare a string field @@ -233,17 +251,16 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, \param data String value to be copied into the field \return nothing */ - #define ast_string_field_ptr_set(x, ptr, data) do { \ const char *__d__ = (data); \ - size_t __dlen__ = (__d__) ? strlen(__d__) : 0; \ - const char **__p__ = (const char **)(ptr); \ - if (__dlen__ == 0) \ + size_t __dlen__ = (__d__) ? strlen(__d__) + 1 : 1; \ + const char **__p__ = (const char **) (ptr); \ + if (__dlen__ == 1) \ *__p__ = __ast_string_field_empty; \ - else if (__dlen__ <= strlen(*__p__)) \ - strcpy((char *)*__p__, __d__); \ - else if ( (*__p__ = __ast_string_field_alloc_space(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__ + 1) ) ) \ - strcpy((char *)*__p__, __d__); \ + else if (!__ast_string_field_ptr_grow(&(x)->__field_mgr, __dlen__, ptr)) \ + memcpy((char *) *__p__, __d__, __dlen__); \ + else if ((*__p__ = __ast_string_field_alloc_space(&(x)->__field_mgr, &(x)->__field_mgr_pool, __dlen__))) \ + memcpy((char *) *__p__, __d__, __dlen__); \ } while (0) /*! @@ -253,11 +270,10 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, \param data String value to be copied into the field \return nothing */ -#define ast_string_field_set(x, field, data) do { \ +#define ast_string_field_set(x, field, data) do { \ ast_string_field_ptr_set(x, &(x)->field, data); \ } while (0) - /*! \brief Set a field to a complex (built) value \param x Pointer to a structure containing fields diff --git a/main/utils.c b/main/utils.c index f156770ca951ec1e830b7273654214e97cd3c709..eff270fba0b1b37109b31a8c6c5970a298212a64 100644 --- a/main/utils.c +++ b/main/utils.c @@ -1425,7 +1425,8 @@ const char __ast_string_field_empty[] = ""; /*!< the empty string */ * 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) + struct ast_string_field_pool **pool_head, + size_t size) { struct ast_string_field_pool *pool; @@ -1436,6 +1437,7 @@ static int add_string_pool(struct ast_string_field_mgr *mgr, *pool_head = pool; mgr->size = size; mgr->used = 0; + mgr->last_alloc = NULL; return 0; } @@ -1451,14 +1453,16 @@ static int add_string_pool(struct ast_string_field_mgr *mgr, * This must be done before destroying the object. */ int __ast_string_field_init(struct ast_string_field_mgr *mgr, - struct ast_string_field_pool **pool_head, int size) + struct ast_string_field_pool **pool_head, + int size) { - const char **p = (const char **)pool_head + 1; + const char **p = (const char **) pool_head + 1; struct ast_string_field_pool *cur = *pool_head; /* clear fields - this is always necessary */ - while ((struct ast_string_field_mgr *)p != mgr) + while ((struct ast_string_field_mgr *) p != mgr) *p++ = __ast_string_field_empty; + mgr->last_alloc = NULL; if (size > 0) { /* allocate the initial pool */ *pool_head = NULL; return add_string_pool(mgr, pool_head, size); @@ -1474,16 +1478,19 @@ int __ast_string_field_init(struct ast_string_field_mgr *mgr, (*pool_head)->prev = NULL; mgr->used = 0; } + while (cur) { struct ast_string_field_pool *prev = cur->prev; + ast_free(cur); cur = prev; } + 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) + struct ast_string_field_pool **pool_head, size_t needed) { char *result = NULL; size_t space = mgr->size - mgr->used; @@ -1500,17 +1507,41 @@ ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr result = (*pool_head)->base + mgr->used; mgr->used += needed; + mgr->last_alloc = result; return result; } +int __ast_string_field_ptr_grow(struct ast_string_field_mgr *mgr, size_t needed, + const ast_string_field *ptr) +{ + int grow = needed - (strlen(*ptr) + 1); + size_t space = mgr->size - mgr->used; + + if (grow <= 0) { + return 0; + } + + if (*ptr != mgr->last_alloc) { + return 1; + } + + if (space < grow) { + return 1; + } + + mgr->used += grow; + + return 0; +} + __attribute((format (printf, 4, 0))) void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr, - struct ast_string_field_pool **pool_head, - const ast_string_field *ptr, const char *format, va_list ap1, va_list ap2) + struct ast_string_field_pool **pool_head, + const ast_string_field *ptr, const char *format, va_list ap1, va_list ap2) { size_t needed; char *dst = (*pool_head)->base + mgr->used; - const char **p = (const char **)ptr; + const char **p = (const char **) ptr; size_t space = mgr->size - mgr->used; /* try to write using available space */ @@ -1531,14 +1562,14 @@ void __ast_string_field_ptr_build_va(struct ast_string_field_mgr *mgr, vsprintf(dst, format, ap2); } - *p = dst; + mgr->last_alloc = *p = dst; mgr->used += needed; } __attribute((format (printf, 4, 5))) void __ast_string_field_ptr_build(struct ast_string_field_mgr *mgr, - struct ast_string_field_pool **pool_head, - const ast_string_field *ptr, const char *format, ...) + struct ast_string_field_pool **pool_head, + const ast_string_field *ptr, const char *format, ...) { va_list ap1, ap2;