[Asterisk-code-review] stringfields: Refactor to allow fields to be added to the e... (asterisk[master])

Joshua Colp asteriskteam at digium.com
Tue Apr 5 11:40:40 CDT 2016


Joshua Colp has submitted this change and it was merged.

Change subject: stringfields:  Refactor to allow fields to be added to the end of structures
......................................................................


stringfields:  Refactor to allow fields to be added to the end of structures

String fields are great, except that you can't add new ones without breaking
ABI compatibility because it shifts down everything else in the structure.
The only alternative is to add your own char * field to the end of the
structure and manage the memory yourself which isn't ideal, especially since
you then can't use the OPT_STRINGFIELD_T type.

Background:

The reason string fields had to be declared inside the
AST_DECLARE_STRING_FIELDS block was to facilitate iteration over all declared
fields for initialization, compare and copy.  Since AST_DECLARE_STRING_FIELDS
declared the pool, then the fields, then the manager, you could use the offsets
of the pool and manager and iterate over the sequential addresses in between to
access the fields. The actual pool, field allocation and field set operations
don't actually care where the field is.  It's just iteration over the fields
that was the problem.

Solution: Extended String Fields

An extended string field is one that is declared outside the
AST_DECLARE_STRING_FIELDS block but still (anywhere) inside the parent
structure.  Other than using AST_STRING_FIELD_EXTENDED instead of
AST_STRING_FIELD, it looks the same as other string fields.  It's storage comes
from the pool and it participates in string field compare and copy operations
peformed on the parent structure. It's also a valid target for the
OPT_STRINGFIELD_T aco option type.

Implementation:

To keep track of the extended fields and make sure that ABI isn't broken, the
existing embedded_pool pointer in the manager structure was repurposed to be a
pointer to a separate header structure that contains the embedded_pool pointer
plus a vector of fields.  The length of the manager structure didn't change and
the embedded_pool pointer isn't used in the macros, only the stringfields C
code.  A side benefit of this is that changing the header structure in the
future won't break ABI.

ast_string_fields_init initializes the normal string fields and appends them to
the vector, and subsequent calls to ast_string_field_init_extended initialize
and append the extended fields. Cleanup, ast_string_fields_cmp, and
ast_string_fields_copy can now work on the vector instead of sequentially
traversing the addresses between the pool and manager.

The total size of a structure using string fields didn't change, whether using
extended fields or not, nor have the offsets of any structure members, either
inside the original block or outside.  Adding an extended field to the end of a
structure is the same as adding a char *.

Details:

The stringfield C code was pulled out from utils.c and into stringfields.c.
It just made sense.

Additional work was done in ast_string_field_init and
ast_calloc_with_stringfields to handle the allocation of the new header
structure and the vector, and the associated cleanup.  In the process some
additional NULL pointer checking was added.

A lot of work was done in stringfields.h since the logic for compare and copy
is there.  Documentation was added as well as somne additional NULL checking.

The ability to call ast_calloc_with_stringfields with a number of structures
greater than 1 never really worked.  Well, the calloc worked but there was no
way to access the additional structures or clean them up.  It was agreed that
there was no use case for requesting more than 1 structure so an ast_assert
was added to prevent it and the iteration code removed.

Testing:

The stringfield unit tests were updated to test both normal and extended
fields.  Tests for ast_string_field_ptr_set_by_fields and
ast_calloc_with_stringfields were also added.

As an ABI test, 13 was compiled from git and the res_pjsip_* modules, except
res_pjsip itself, saved off.  The patch was then added and a full compile and
install was performed.  Then the older res_pjsip_* moduled were copied over the
installed versions so res_pjsip was new and the rest were old.  No issues.

contact->aor, which is a char * at the end of contact, was then changed to an
extended string field and a recompile and reinstall was performed, again
leaving stock versions of the the res_pjsip_* modules.  Again, no issues with
the res_pjsip_* modules using the old stringfield implementation and with
contact->aor as a char *, and res_pjsip itself using the new stringfield
implementation and contact->aor being an extended string field.

Finally, several existing string fields were converted to extended string
fields to test OPT_STRINGFIELD_T.  Again, no issues.

Change-Id: I235db338c5b178f5a13b7946afbaa5d4a0f91d61
---
M include/asterisk/stringfields.h
A main/stringfields.c
M main/utils.c
M tests/test_stringfields.c
4 files changed, 908 insertions(+), 471 deletions(-)

Approvals:
  Richard Mudgett: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, approved; Verified



diff --git a/include/asterisk/stringfields.h b/include/asterisk/stringfields.h
index d0879b2..c24424b 100644
--- a/include/asterisk/stringfields.h
+++ b/include/asterisk/stringfields.h
@@ -17,6 +17,7 @@
  */
 
 /*! \file
+  \page Stringfields String Fields
   \brief String fields in structures
 
   This file contains objects and macros used to manage string
@@ -93,6 +94,80 @@
   ast_free(x);
   \endcode
 
+  A new feature "Extended String Fields" has been added in 13.9.0.
+
+  An extended field is one that is declared outside the AST_DECLARE_STRING_FIELDS
+  block but still inside the parent structure.  It's most useful for extending
+  structures where adding a new string field to an existing AST_DECLARE_STRING_FIELDS
+  block would break ABI compatibility.
+
+  Example:
+
+  \code
+  struct original_structure_version {
+      AST_DECLARE_STRING_FIELDS(
+          AST_STRING_FIELD(foo);
+          AST_STRING_FIELD(bar);
+      );
+      int x1;
+      int x2;
+  };
+  \endcode
+
+  Adding "blah" to the existing string fields breaks ABI compatibility because it changes
+  the offsets of x1 and x2.
+
+  \code
+  struct new_structure_version {
+      AST_DECLARE_STRING_FIELDS(
+          AST_STRING_FIELD(foo);
+          AST_STRING_FIELD(bar);
+          AST_STRING_FIELD(blah);
+      );
+      int x1;
+      int x2;
+  };
+  \endcode
+
+  However, adding "blah" as an extended string field to the end of the structure doesn't break
+  ABI compatibility but still allows the use of the existing pool.
+
+  \code
+  struct new_structure_version {
+      AST_DECLARE_STRING_FIELDS(
+          AST_STRING_FIELD(foo);
+          AST_STRING_FIELD(bar);
+      );
+      int x1;
+      int x2;
+      AST_STRING_FIELD_EXTENDED(blah);
+  };
+  \endcode
+
+  The only additional step required is to call ast_string_field_init_extended so the
+  pool knows about the new field.  It must be called AFTER ast_string_field_init or
+  ast_calloc_with_stringfields.  Although ast_calloc_with_stringfields is used in the
+  sample below, it's not necessary for extended string fields.
+
+  \code
+
+  struct new_structure_version *x = ast_calloc_with_stringfields(1, struct new_structure_version, 252);
+  if (!x) {
+      return;
+  }
+
+  ast_string_field_init_extended(x, blah);
+  \endcode
+
+  The new field can now be treated just like any other string field and it's storage will
+  be released with the rest of the string fields.
+
+  \code
+  ast_string_field_set(x, foo, "infinite loop");
+  ast_stringfield_free_memory(x);
+  ast_free(x);
+  \endcode
+
   This completes the API description.
 */
 
@@ -100,6 +175,7 @@
 #define _ASTERISK_STRINGFIELDS_H
 
 #include "asterisk/inline_api.h"
+#include "asterisk/vector.h"
 
 /*!
   \internal
@@ -139,11 +215,28 @@
 
 /*!
   \internal
+  \brief The definition for the string field vector used for compare and copy
+  \since 13.9.0
+*/
+AST_VECTOR(ast_string_field_vector, const char **);
+
+/*!
+  \internal
+  \brief Structure used to hold a pointer to the embedded pool and the field vector
+  \since 13.9.0
+*/
+struct ast_string_field_header {
+	struct ast_string_field_pool *embedded_pool;	/*!< pointer to the embedded pool, if any */
+	struct ast_string_field_vector string_fields;	/*!< field vector for compare and copy */
+};
+
+/*!
+  \internal
   \brief Structure used to manage the storage for a set of string fields.
 */
 struct ast_string_field_mgr {
 	ast_string_field last_alloc;			/*!< the last field allocated */
- 	struct ast_string_field_pool *embedded_pool;	/*!< pointer to the embedded pool, if any */
+	struct ast_string_field_header *header;	/*!< pointer to the header */
 #if defined(__AST_DEBUG_MALLOC)
 	const char *owner_file;				/*!< filename of owner */
 	const char *owner_func;				/*!< function name of owner */
@@ -218,6 +311,29 @@
 #define AST_STRING_FIELD(name) const ast_string_field name
 
 /*!
+  \brief Declare an extended string field
+  \since 13.9.0
+
+  \param name The field name
+*/
+#define AST_STRING_FIELD_EXTENDED(name) AST_STRING_FIELD(name)
+
+enum ast_stringfield_cleanup_type {
+	/*!
+	 * Reset all string fields and free all extra pools that may have been created
+	 * The allocation or structure can be reused as is.
+	 */
+	AST_STRINGFIELD_RESET = 0,
+	/*!
+	 * Reset all string fields and free all pools.
+	 * If the pointer was returned by ast_calloc_with_stringfields, it can NOT be reused
+	 * and should be immediately freed.  Otherwise, you must call ast_string_field_init
+	 * again if you want to reuse it.
+	 */
+	AST_STRINGFIELD_DESTROY = -1,
+};
+
+/*!
   \brief Declare the fields needed in a structure
   \param field_list The list of fields to declare, using AST_STRING_FIELD() for each one.
   Internally, string fields are stored as a pointer to the head of the pool,
@@ -239,17 +355,65 @@
   \brief Initialize a field pool and fields
   \param x Pointer to a structure containing fields
   \param size Amount of storage to allocate.
-	Use 0 to reset fields to the default value,
+	Use AST_STRINGFIELD_RESET to reset fields to the default value,
 	and release all but the most recent pool.
-	size<0 (used internally) means free all pools.
+	AST_STRINGFIELD_DESTROY (used internally) means free all pools which is
+	equivalent to calling ast_string_field_free_memory.
+
   \return 0 on success, non-zero on failure
 */
 #define ast_string_field_init(x, size) \
-	__ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__res__ = __ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, size, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+	} \
+	__res__ ; \
+})
 
-/*! \brief free all memory - to be called before destroying the object */
-#define ast_string_field_free_memory(x)	\
-	__ast_string_field_init(&(x)->__field_mgr, &(x)->__field_mgr_pool, -1, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+/*!
+ * \brief free all memory - to be called before destroying the object
+ *
+ * \param x
+ *
+ */
+#define ast_string_field_free_memory(x)	 \
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__res__ = __ast_string_field_free_memory(&(x)->__field_mgr, &(x)->__field_mgr_pool, \
+			AST_STRINGFIELD_DESTROY, __FILE__, __LINE__, __PRETTY_FUNCTION__); \
+	} \
+	__res__; \
+})
+
+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);
+
+/*!
+ * \brief Initialize an extended string field
+ * \since 13.9.0
+ *
+ * \param x Pointer to a structure containing the field
+ * \param field The extended field to initialize
+ * \retval zero on success
+ * \retval non-zero on error
+ *
+ * \note
+ * This macro must be called on ALL fields defined with AST_STRING_FIELD_EXTENDED after
+ * ast_string_field_init has been called.
+ */
+#define ast_string_field_init_extended(x, field) \
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL && (x)->__field_mgr.header != NULL) { \
+		ast_string_field *non_const = (ast_string_field *)&(x)->field; \
+		*non_const = __ast_string_field_empty; \
+		__res__ = AST_VECTOR_APPEND(&(x)->__field_mgr.header->string_fields, non_const); \
+	} \
+	__res__; \
+})
 
 /*!
  * \internal
@@ -260,9 +424,10 @@
 
 /*!
  * \brief Allocate a structure with embedded stringfields in a single allocation
- * \param n Number of structures to allocate (see ast_calloc)
+ * \param n Current imlementation only allows 1 structure to be allocated
  * \param type The type of structure to allocate
  * \param size The number of bytes of space (minimum) to allocate for stringfields to use
+ *             in each structure
  *
  * This function will allocate memory for one or more structures that use stringfields, and
  * also allocate space for the stringfields and initialize the stringfield management
@@ -271,16 +436,16 @@
  * \since 1.8
  */
 #define ast_calloc_with_stringfields(n, type, size) \
-	__ast_calloc_with_stringfields(n, sizeof(type), offsetof(type, __field_mgr), offsetof(type, __field_mgr_pool), \
-				       size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
+	__ast_calloc_with_stringfields(n, sizeof(type), offsetof(type, __field_mgr), \
+		offsetof(type, __field_mgr_pool), size, __FILE__, __LINE__, __PRETTY_FUNCTION__)
 
 /*!
  * \internal
  * \brief internal version of ast_calloc_with_stringfields
  */
-void * attribute_malloc __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);
+void * attribute_malloc __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);
 
 /*!
   \internal
@@ -314,7 +479,14 @@
   \retval zero on success
   \retval non-zero on error
 */
-#define ast_string_field_ptr_set(x, ptr, data) ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data)
+#define ast_string_field_ptr_set(x, ptr, data) \
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__res__ = ast_string_field_ptr_set_by_fields((x)->__field_mgr_pool, (x)->__field_mgr, ptr, data); \
+	} \
+	__res__; \
+})
 
 #define ast_string_field_ptr_set_by_fields(field_mgr_pool, field_mgr, ptr, data)               \
 ({                                                                                             \
@@ -348,7 +520,14 @@
   \retval zero on success
   \retval non-zero on error
 */
-#define ast_string_field_set(x, field, data) ast_string_field_ptr_set(x, &(x)->field, data)
+#define ast_string_field_set(x, field, data) \
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__res__ = ast_string_field_ptr_set(x, &(x)->field, data); \
+	} \
+	__res__; \
+})
 
 /*!
   \brief Set a field to a complex (built) value
@@ -359,7 +538,14 @@
   \return nothing
 */
 #define ast_string_field_ptr_build(x, ptr, fmt, args...) \
-	__ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args)
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args); \
+		__res__ = 0; \
+	} \
+	__res__; \
+})
 
 /*!
   \brief Set a field to a complex (built) value
@@ -370,7 +556,14 @@
   \return nothing
 */
 #define ast_string_field_build(x, field, fmt, args...) \
-	__ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args)
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__ast_string_field_ptr_build(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args); \
+		__res__ = 0; \
+	} \
+	__res__; \
+})
 
 /*!
   \brief Set a field to a complex (built) value with prebuilt va_lists.
@@ -381,7 +574,14 @@
   \return nothing
 */
 #define ast_string_field_ptr_build_va(x, ptr, fmt, args) \
-	__ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args)
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) ptr, fmt, args); \
+		__res__ = 0; \
+	} \
+	__res__; \
+})
 
 /*!
   \brief Set a field to a complex (built) value
@@ -392,7 +592,14 @@
   \return nothing
 */
 #define ast_string_field_build_va(x, field, fmt, args) \
-	__ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args)
+({ \
+	int __res__ = -1; \
+	if (((void *)(x)) != NULL) { \
+		__ast_string_field_ptr_build_va(&(x)->__field_mgr, &(x)->__field_mgr_pool, (ast_string_field *) &(x)->field, fmt, args); \
+		__res__ = 0; \
+	} \
+	__res__; \
+})
 
 /*!
   \brief Compare the string fields in two instances of the same structure
@@ -404,24 +611,15 @@
 */
 #define ast_string_fields_cmp(instance1, instance2) \
 ({ \
-	int __res__ = 0; \
-	size_t __ptr_size__ = sizeof(char *); \
-	int __len__ = ((void *)&(instance1)->__field_mgr - (void *)&(instance1)->__field_mgr_pool)/__ptr_size__ - 1; \
-	int __len2__ = ((void *)&(instance2)->__field_mgr - (void *)&(instance2)->__field_mgr_pool)/__ptr_size__ - 1; \
-	if (__len__ == __len2__) { \
-		char **__head1__ = (void *)&(instance1)->__field_mgr_pool + __ptr_size__; \
-		char **__head2__ = (void *)&(instance2)->__field_mgr_pool + __ptr_size__; \
-		for (__len__ -= 1; __len__ >= 0; __len__--) { \
-			__res__ = strcmp(__head1__[__len__], __head2__[__len__]); \
-			if (__res__) { \
-				break; \
-			} \
-		} \
-	} else { \
-		__res__ = -1; \
+	int __res__ = -1; \
+	if (((void *)(instance1)) != NULL && ((void *)(instance2)) != NULL) { \
+		__res__ = __ast_string_fields_cmp(&(instance1)->__field_mgr.header->string_fields, \
+			&(instance2)->__field_mgr.header->string_fields); \
 	} \
 	__res__; \
 })
+
+int __ast_string_fields_cmp(struct ast_string_field_vector *left, struct ast_string_field_vector *right);
 
 /*!
   \brief Copy all string fields from one instance to another of the same structure
@@ -433,27 +631,16 @@
 */
 #define ast_string_fields_copy(copy, orig) \
 ({ \
-	int __outer_res__ = 0; \
-	size_t __ptr_size__ = sizeof(char *); \
-	int __len__ = ((void *)&(copy)->__field_mgr - (void *)&(copy)->__field_mgr_pool)/__ptr_size__ - 1; \
-	int __len2__ = ((void *)&(orig)->__field_mgr - (void *)&(orig)->__field_mgr_pool)/__ptr_size__ - 1; \
-	if (__len__ == __len2__) { \
-		ast_string_field *__copy_head__ = (void *)&(copy)->__field_mgr_pool + __ptr_size__; \
-		ast_string_field *__orig_head__ = (void *)&(orig)->__field_mgr_pool + __ptr_size__; \
-		for (__len2__ -= 1; __len2__ >= 0; __len2__--) { \
-			__ast_string_field_release_active((copy)->__field_mgr_pool, __copy_head__[__len2__]); \
-			__copy_head__[__len2__] = __ast_string_field_empty; \
-		} \
-		for (__len__ -= 1; __len__ >= 0; __len__--) { \
-			if (ast_string_field_ptr_set((copy), &__copy_head__[__len__], __orig_head__[__len__])) { \
-				__outer_res__ = -1; \
-				break; \
-			} \
-		} \
-	} else { \
-		__outer_res__ = -1; \
+	int __res__ = -1; \
+	if (((void *)(copy)) != NULL && ((void *)(orig)) != NULL) { \
+		__res__ = __ast_string_fields_copy(((copy)->__field_mgr_pool), \
+			(struct ast_string_field_mgr *)&((copy)->__field_mgr), \
+			(struct ast_string_field_mgr *)&((orig)->__field_mgr)); \
 	} \
-	__outer_res__; \
+	__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);
+
 #endif /* _ASTERISK_STRINGFIELDS_H */
diff --git a/main/stringfields.c b/main/stringfields.c
new file mode 100644
index 0000000..67dd06c
--- /dev/null
+++ b/main/stringfields.c
@@ -0,0 +1,508 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Digium, Inc.
+ *
+ * Kevin P. Fleming <kpfleming at 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 at 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;
+}
diff --git a/main/utils.c b/main/utils.c
index e92f5c3..0983975 100644
--- a/main/utils.c
+++ b/main/utils.c
@@ -61,9 +61,6 @@
 #include "asterisk/time.h"
 
 #define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
-#include "asterisk/stringfields.h"
-
-#define AST_API_MODULE		/* ensure that inlinable API functions will be built in this module if required */
 #include "asterisk/utils.h"
 
 #define AST_API_MODULE
@@ -2022,374 +2019,6 @@
 
 	return res;
 }
-
-/*
- * stringfields support routines.
- */
-
-/* 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;
-}
-
-/*! \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 defined(__AST_DEBUG_MALLOC)
-	if (!(pool = __ast_calloc(1, alloc_size, file, lineno, func))) {
-		return -1;
-	}
-#else
-	if (!(pool = ast_calloc(1, alloc_size))) {
-		return -1;
-	}
-#endif
-
-	pool->prev = *pool_head;
-	pool->size = alloc_size - sizeof(*pool);
-	*pool_head = pool;
-	mgr->last_alloc = NULL;
-
-	return 0;
-}
-
-/*
- * This is an internal API, code should not use it directly.
- * It initializes all fields as empty, then uses 'size' for 3 functions:
- * size > 0 means initialize the pool list with a pool of given size.
- *	This must be called right after allocating the object.
- * size = 0 means release all pools except the most recent one.
- *      If the first pool was allocated via embedding in another
- *      object, that pool will be preserved instead.
- *	This is useful to e.g. reset an object to the initial value.
- * size < 0 means release all pools.
- *	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 needed, const char *file, int lineno, const char *func)
-{
-	const char **p = (const char **) pool_head + 1;
-	struct ast_string_field_pool *cur = NULL;
-	struct ast_string_field_pool *preserve = NULL;
-
-	/* clear fields - this is always necessary */
-	while ((struct ast_string_field_mgr *) p != mgr) {
-		*p++ = __ast_string_field_empty;
-	}
-
-	mgr->last_alloc = NULL;
-#if defined(__AST_DEBUG_MALLOC)
-	mgr->owner_file = file;
-	mgr->owner_func = func;
-	mgr->owner_line = lineno;
-#endif
-	if (needed > 0) {		/* allocate the initial pool */
-		*pool_head = NULL;
-		mgr->embedded_pool = NULL;
-		return add_string_pool(mgr, pool_head, needed, file, lineno, func);
-	}
-
-	/* if there is an embedded pool, we can't actually release *all*
-	 * pools, we must keep the embedded one. if the caller is about
-	 * to free the structure that contains the stringfield manager
-	 * and embedded pool anyway, it will be freed as part of that
-	 * operation.
-	 */
-	if ((needed < 0) && mgr->embedded_pool) {
-		needed = 0;
-	}
-
-	if (needed < 0) {		/* reset all pools */
-		cur = *pool_head;
-	} else if (mgr->embedded_pool) { /* preserve the embedded pool */
-		preserve = mgr->embedded_pool;
-		cur = *pool_head;
-	} else {			/* preserve the last pool */
-		if (*pool_head == NULL) {
-			ast_log(LOG_WARNING, "trying to reset empty pool\n");
-			return -1;
-		}
-		preserve = *pool_head;
-		cur = preserve->prev;
-	}
-
-	if (preserve) {
-		preserve->prev = NULL;
-		preserve->used = preserve->active = 0;
-	}
-
-	while (cur) {
-		struct ast_string_field_pool *prev = cur->prev;
-
-		if (cur != preserve) {
-			ast_free(cur);
-		}
-		cur = prev;
-	}
-
-	*pool_head = preserve;
-
-	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;
-	unsigned int x;
-
-#if defined(__AST_DEBUG_MALLOC)
-	if (!(allocation = __ast_calloc(num_structs, size_to_alloc, file, lineno, func))) {
-		return NULL;
-	}
-#else
-	if (!(allocation = ast_calloc(num_structs, size_to_alloc))) {
-		return NULL;
-	}
-#endif
-
-	for (x = 0; x < num_structs; x++) {
-		void *base = allocation + (size_to_alloc * x);
-		const char **p;
-
-		mgr = base + field_mgr_offset;
-		pool_head = base + field_mgr_pool_offset;
-		pool = base + struct_size;
-
-		p = (const char **) pool_head + 1;
-		while ((struct ast_string_field_mgr *) p != mgr) {
-			*p++ = __ast_string_field_empty;
-		}
-
-		mgr->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;
-}
-
-/* end of stringfields support */
 
 AST_MUTEX_DEFINE_STATIC(fetchadd_m); /* used for all fetc&add ops */
 
diff --git a/tests/test_stringfields.c b/tests/test_stringfields.c
index 2eeb83d..e57f8d7 100644
--- a/tests/test_stringfields.c
+++ b/tests/test_stringfields.c
@@ -24,7 +24,6 @@
  *
  * Test module for string fields API
  * \ingroup tests
- * \todo need to test ast_calloc_with_stringfields
  */
 
 /*** MODULEINFO
@@ -50,16 +49,16 @@
 	struct {
 		AST_DECLARE_STRING_FIELDS (
 			AST_STRING_FIELD(string1);
-			AST_STRING_FIELD(string2);
 		);
+		AST_STRING_FIELD_EXTENDED(string2);
 	} test_struct;
 
 	struct {
 		AST_DECLARE_STRING_FIELDS (
 			AST_STRING_FIELD(string1);
 			AST_STRING_FIELD(string2);
-			AST_STRING_FIELD(string3);
 		);
+		AST_STRING_FIELD_EXTENDED(string3);
 	} test_struct2;
 
 	switch (cmd) {
@@ -74,6 +73,9 @@
 		break;
 	}
 
+	memset(&test_struct, 0, sizeof(test_struct));
+	memset(&test_struct2, 0, sizeof(test_struct));
+
 	ast_test_status_update(test, "First things first. Let's see if we can actually allocate string fields\n");
 
 	if (ast_string_field_init(&test_struct, 32)) {
@@ -82,6 +84,7 @@
 	} else {
 		ast_test_status_update(test, "All right! Successfully allocated! Now let's get down to business\n");
 	}
+	ast_string_field_init_extended(&test_struct, string2);
 
 	ast_test_status_update(test,"We're going to set some string fields and perform some checks\n");
 
@@ -255,6 +258,8 @@
 
 	ast_string_field_init(&test_struct2, 32);
 	ast_test_status_update(test, "Now using a totally separate area of memory we're going to test a basic pool freeing scenario\n");
+	ast_string_field_init_extended(&test_struct2, string3);
+
 
 	ast_string_field_set(&test_struct2, string1, "first");
 	ast_string_field_set(&test_struct2, string2, "second");
@@ -294,15 +299,22 @@
 	return AST_TEST_FAIL;
 }
 
+struct test_struct {
+	int foo;
+	AST_DECLARE_STRING_FIELDS (
+		AST_STRING_FIELD(string1);
+	);
+	int foo2;
+	AST_STRING_FIELD_EXTENDED(string2);
+};
+
 AST_TEST_DEFINE(string_field_aggregate_test)
 {
-	struct test_struct {
-		AST_DECLARE_STRING_FIELDS (
-			AST_STRING_FIELD(string1);
-			AST_STRING_FIELD(string2);
-		);
-		int foo;
-	} inst1, inst2, inst3, inst4;
+	enum ast_test_result_state res = AST_TEST_PASS;
+	struct test_struct *inst1 = NULL;
+	struct test_struct *inst2 = NULL;
+	struct test_struct *inst3 = NULL;
+	struct test_struct *inst4 = NULL;
 
 	switch (cmd) {
 	case TEST_INIT:
@@ -316,88 +328,189 @@
 		break;
 	}
 
-	ast_string_field_init(&inst1, 32);
-	ast_string_field_init(&inst2, 32);
-	ast_string_field_init(&inst3, 32);
-	ast_string_field_init(&inst4, 32);
+	inst1 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+	if (!inst1) {
+		ast_test_status_update(test, "Unable to allocate structure 1!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+	ast_string_field_init_extended(inst1, string2);
 
-	ast_string_field_set(&inst1, string1, "foo");
-	ast_string_field_set(&inst1, string2, "bar");
-	inst1.foo = 1;
+	inst2 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+	if (!inst2) {
+		ast_test_status_update(test, "Unable to allocate structure 2!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+	ast_string_field_init_extended(inst2, string2);
 
-	ast_string_field_set(&inst2, string2, "bar");
-	ast_string_field_set(&inst2, string1, "foo");
-	inst2.foo = 2;
+	inst3 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+	if (!inst3) {
+		ast_test_status_update(test, "Unable to allocate structure 3!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+	ast_string_field_init_extended(inst3, string2);
 
-	ast_string_field_set(&inst3, string1, "foo");
-	ast_string_field_set(&inst3, string2, "baz");
-	inst3.foo = 3;
+	inst4 = ast_calloc_with_stringfields(1, struct test_struct, 32);
+	if (!inst4) {
+		ast_test_status_update(test, "Unable to allocate structure 4!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+	ast_string_field_init_extended(inst4, string2);
 
-	ast_string_field_set(&inst4, string1, "faz");
-	ast_string_field_set(&inst4, string2, "baz");
-	inst4.foo = 3;
 
-	if (ast_string_fields_cmp(&inst1, &inst2)) {
+	ast_string_field_set(inst1, string1, "foo");
+	ast_string_field_set(inst1, string2, "bar");
+	inst1->foo = 1;
+
+	ast_string_field_ptr_set_by_fields(inst2->__field_mgr_pool, inst2->__field_mgr, &inst2->string2, "bar");
+	ast_string_field_ptr_set_by_fields(inst2->__field_mgr_pool, inst2->__field_mgr, &inst2->string1, "foo");
+	inst2->foo = 2;
+
+	if (inst3->__field_mgr.header->embedded_pool->prev) {
+		ast_test_status_update(test, "Structure 3 embedded pool should not have a previous pool!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+
+	ast_string_field_set(inst3, string1, "foo");
+
+	if (inst3->__field_mgr.header->embedded_pool != inst3->__field_mgr_pool) {
+		ast_test_status_update(test, "Structure 3 embedded pool should have been the current pool!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+
+	if (inst3->__field_mgr.header->embedded_pool->prev) {
+		ast_test_status_update(test, "Structure 3 embedded pool should not have a previous pool!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+
+	ast_test_status_update(test, "Structures 3 embedded pool initialized successfully.\n");
+
+	/* Exhaust the embedded pool */
+	ast_string_field_set(inst3, string2, "baz 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890");
+	inst3->foo = 3;
+
+	if (inst3->__field_mgr_pool == inst3->__field_mgr.header->embedded_pool) {
+		ast_test_status_update(test, "Structure 3 embedded pool should not have been the current pool!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+
+	if (inst3->__field_mgr.header->embedded_pool != inst3->__field_mgr_pool->prev) {
+		ast_test_status_update(test, "Structure 3 embedded pool should be the current pool's previous!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	}
+
+	ast_test_status_update(test, "Structures 3 additional pool initialized successfully.\n");
+
+	ast_string_field_set(inst4, string1, "faz");
+	/* Exhaust the embedded pool */
+	ast_string_field_set(inst4, string2, "baz 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890 1234567890");
+	inst4->foo = 4;
+
+	if (ast_string_fields_cmp(inst1, inst2)) {
 		ast_test_status_update(test, "Structures 1/2 should be equal!\n");
+		res = AST_TEST_FAIL;
 		goto error;
 	} else {
 		ast_test_status_update(test, "Structures 1/2 are equal as expected.\n");
 	}
 
-	if (!ast_string_fields_cmp(&inst1, &inst3)) {
+	if (!ast_string_fields_cmp(inst1, inst3)) {
 		ast_test_status_update(test, "Structures 1/3 should be different!\n");
+		res = AST_TEST_FAIL;
 		goto error;
 	} else {
 		ast_test_status_update(test, "Structures 1/3 are different as expected.\n");
 	}
 
-	if (!ast_string_fields_cmp(&inst2, &inst3)) {
+	if (!ast_string_fields_cmp(inst2, inst3)) {
 		ast_test_status_update(test, "Structures 2/3 should be different!\n");
+		res = AST_TEST_FAIL;
 		goto error;
 	} else {
 		ast_test_status_update(test, "Structures 2/3 are different as expected.\n");
 	}
 
-	if (!ast_string_fields_cmp(&inst3, &inst4)) {
+	if (!ast_string_fields_cmp(inst3, inst4)) {
 		ast_test_status_update(test, "Structures 3/4 should be different!\n");
+		res = AST_TEST_FAIL;
 		goto error;
 	} else {
 		ast_test_status_update(test, "Structures 3/4 are different as expected.\n");
 	}
 
-	if (ast_string_fields_copy(&inst1, &inst3)) {
-		ast_test_status_update(test, "Copying from structure 3 to structure 4 failed!\n");
+	if (ast_string_fields_copy(inst1, inst3)) {
+		ast_test_status_update(test, "Copying from structure 3 to structure 1 failed!\n");
+		res = AST_TEST_FAIL;
 		goto error;
 	} else {
-		ast_test_status_update(test, "Copying from structure 3 to structure 4 succeeded!\n");
+		ast_test_status_update(test, "Copying from structure 3 to structure 1 succeeded!\n");
 	}
 
 	/* inst1 and inst3 should now be equal and inst1 should no longer be equal to inst2 */
-	if (ast_string_fields_cmp(&inst1, &inst3)) {
+	if (ast_string_fields_cmp(inst1, inst3)) {
 		ast_test_status_update(test, "Structures 1/3 should be equal!\n");
+		res = AST_TEST_FAIL;
 		goto error;
 	} else {
 		ast_test_status_update(test, "Structures 1/3 are equal as expected.\n");
 	}
 
-	if (!ast_string_fields_cmp(&inst1, &inst2)) {
+	if (!ast_string_fields_cmp(inst1, inst2)) {
 		ast_test_status_update(test, "Structures 1/2 should be different!\n");
-		goto error;
+		res = AST_TEST_FAIL;
 	} else {
 		ast_test_status_update(test, "Structures 1/2 are different as expected.\n");
 	}
 
-	ast_string_field_free_memory(&inst1);
-	ast_string_field_free_memory(&inst2);
-	ast_string_field_free_memory(&inst3);
-	ast_string_field_free_memory(&inst4);
-	return AST_TEST_PASS;
+	ast_test_status_update(test, "Reset but don't free.\n");
+
+	ast_string_field_init(inst1, AST_STRINGFIELD_RESET);
+	ast_string_field_init(inst2, AST_STRINGFIELD_RESET);
+	ast_string_field_init(inst3, AST_STRINGFIELD_RESET);
+	ast_string_field_init(inst4, AST_STRINGFIELD_RESET);
+
+	if (ast_string_fields_cmp(inst1, inst2)) {
+		ast_test_status_update(test, "Structures 1/2 should be the same (empty)!\n");
+		res = AST_TEST_FAIL;
+	} else {
+		ast_test_status_update(test, "Structures 1/2 are the same (empty) as expected.\n");
+	}
+
+	if (inst4->__field_mgr.header->embedded_pool != inst4->__field_mgr_pool) {
+		ast_test_status_update(test, "Structure 4 embedded pool should have been the current pool!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	} else {
+		ast_test_status_update(test, "Structure 4 embedded pool is the current pool as expected.\n");
+	}
+
+	if (inst4->__field_mgr.header->embedded_pool->prev) {
+		ast_test_status_update(test, "Structure 4 embedded pool should not have a previous pool!\n");
+		res = AST_TEST_FAIL;
+		goto error;
+	} else {
+		ast_test_status_update(test, "Structure 4 embedded pool does not have a previous as expected.\n");
+	}
+
 error:
-	ast_string_field_free_memory(&inst1);
-	ast_string_field_free_memory(&inst2);
-	ast_string_field_free_memory(&inst3);
-	ast_string_field_free_memory(&inst4);
-	return AST_TEST_FAIL;
+	ast_string_field_free_memory(inst1);
+	ast_free(inst1);
+	ast_string_field_free_memory(inst2);
+	ast_free(inst2);
+	ast_string_field_free_memory(inst3);
+	ast_free(inst3);
+	ast_string_field_free_memory(inst4);
+	ast_free(inst4);
+
+	return res;
 }
 
 static int unload_module(void)

-- 
To view, visit https://gerrit.asterisk.org/2482
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I235db338c5b178f5a13b7946afbaa5d4a0f91d61
Gerrit-PatchSet: 6
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: George Joseph <george.joseph at fairview5.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>



More information about the asterisk-code-review mailing list