/*
 * 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 in structures

  This file contains objects and macros used to manage string
  fields in structures without requiring them to be allocated
  as fixed-size buffers or requiring individual allocations for
  for each field.
  
  Using this functionality is quite simple... an example structure
  with three fields is defined like this:
  
  \code
  struct sample_fields {
	  int x1;
	  AST_DECLARE_STRING_FIELDS(
		  AST_STRING_FIELD(name);
		  AST_STRING_FIELD(address);
		  AST_STRING_FIELD(password);
	  );
	  long x2;
  };
  \endcode
  
  When an instance of this structure is allocated, the fields
  (and the pool of storage for them) must be initialized:
  
  \code
  struct sample_fields *sample;
  
  sample = calloc(1, sizeof(*sample));
  if (sample) {
	  if (ast_string_field_init(sample, 256)) {
		  free(sample);
		  sample = NULL;
	  }
  }
  
  if (!sample) {
  ...
  }
  \endcode
  
  Fields will default to pointing to an empty string, and will
  revert to that when ast_string_field_free() is called. This means
  that a string field will \b never contain NULL.
  
  Using the fields is much like using regular 'char *' fields
  in the structure, except that writing into them must be done
  using wrapper macros defined in this file.
  
  Storing simple values into fields can be done using ast_string_field_set();
  more complex values (using printf-style format strings) can be stored
  using ast_string_field_build().
  
  When the structure instance is no longer needed, the fields
  and their storage pool must be freed:
  
  \code
  ast_string_field_free_memory(sample);
  free(sample);
  \endcode
*/

#ifndef _ASTERISK_STRINGFIELDS_H
#define _ASTERISK_STRINGFIELDS_H

#include <string.h>
#include <stdarg.h>
#include <stddef.h>

#include "asterisk/inline_api.h"
#include "asterisk/compiler.h"
#include "asterisk/compat.h"

/*!
  \internal
  \brief An opaque type for managed string fields in structures

  Don't declare instances of this type directly; use the AST_STRING_FIELD()
  macro instead.
*/
typedef const char * ast_string_field;

/*!
  \internal
  \brief A constant empty string used for fields that have no other value
*/
extern const char __ast_string_field_empty[];

/*!
  \internal
  \brief Structure used to hold a pool of space for string fields
*/
struct ast_string_field_pool {
	struct ast_string_field_pool *prev;	/*!< pointer to the previous pool, if any */
	char base[0];				/*!< storage space for the fields */
};

/*!
  \internal
  \brief Structure used to manage the storage for a set of string fields
*/
struct ast_string_field_mgr {
	struct ast_string_field_pool *pool;	/*!< the address of the pool's structure */
	size_t size;				/*!< the total size of the current pool */
	size_t space;				/*!< the space available in the current pool */
	size_t used;				/*!< the space used in the current pool */
};

/*!
  \internal
  \brief Initialize a field pool manager and fields
  \param mgr Pointer to the pool manager structure
  \param size Amount of storage to allocate
  \param fields Pointer to the first entry of the field array
  \param num_fields Number of fields in the array
  \return 0 on success, non-zero on failure
*/
int __ast_string_field_init(struct ast_string_field_mgr *mgr, size_t size,
			    ast_string_field *fields, int num_fields);

/*!
  \internal
  \brief Allocate space for a field
  \param mgr Pointer to the pool manager structure
  \param needed Amount of space needed for this field
  \param fields Pointer to the first entry of the field array
  \param num_fields Number of fields in the array
  \return NULL on failure, an address for the field on success

  This function will allocate the requested amount of space from
  the field pool. If the requested amount of space is not available,
  an additional pool will be allocated.
*/
ast_string_field __ast_string_field_alloc_space(struct ast_string_field_mgr *mgr, size_t needed,
						ast_string_field *fields, int num_fields);

/*!
  \internal
  \brief Set a field to a complex (built) value
  \param mgr Pointer to the pool manager structure
  \param fields Pointer to the first entry of the field array
  \param num_fields Number of fields in the array
  \param index Index position of the field within the structure
  \param format printf-style format string
  \return nothing
*/
void __ast_string_field_index_build(struct ast_string_field_mgr *mgr,
				    ast_string_field *fields, int num_fields,
				    int index, const char *format, ...) __attribute__((format(printf, 5, 6)));

/*!
  \internal
  \brief Set a field to a complex (built) value
  \param mgr Pointer to the pool manager structure
  \param fields Pointer to the first entry of the field array
  \param num_fields Number of fields in the array
  \param index Index position of the field within the structure
  \param format printf-style format string
  \param args va_list of the args for the format_string
  \param args_again a copy of the first va_list for the sake of bsd not having a copy routine
  \return nothing
*/
void __ast_string_field_index_build_va(struct ast_string_field_mgr *mgr,
				       ast_string_field *fields, int num_fields,
				       int index, const char *format, va_list a1, va_list a2)  __attribute__((format(printf, 5, 0)));

/*!
  \brief Declare a string field
  \param name The field name
*/
#define AST_STRING_FIELD(name) const ast_string_field name

/*!
  \brief Declare the fields needed in a structure
  \param field_list The list of fields to declare, using AST_STRING_FIELD() for each one
*/
#define AST_DECLARE_STRING_FIELDS(field_list) \
	ast_string_field __begin_field[0]; \
	field_list \
	ast_string_field __end_field[0]; \
	struct ast_string_field_mgr __field_mgr

/*!
  \brief Get the number of string fields in a structure
  \param x Pointer to a structure containing fields
  \return the number of fields in the structure's definition
*/
#define ast_string_field_count(x) \
	(offsetof(typeof(*(x)), __end_field) - offsetof(typeof(*(x)), __begin_field)) / sizeof(ast_string_field)

/*!
  \brief Get the index of a field in a structure
  \param x Pointer to a structure containing fields
  \param field Name of the field to locate
  \return the position (index) of the field within the structure's
  array of fields
*/
#define ast_string_field_index(x, field) \
	(offsetof(typeof(*x), field) - offsetof(typeof(*x), __begin_field)) / sizeof(ast_string_field)

/*!
  \brief Initialize a field pool and fields
  \param x Pointer to a structure containing fields
  \param size Amount of storage to allocate
  \return 0 on success, non-zero on failure
*/
#define ast_string_field_init(x, size) \
	__ast_string_field_init(&(x)->__field_mgr, size, &(x)->__begin_field[0], ast_string_field_count(x))

/*!
  \brief Set a field to a simple string value
  \param x Pointer to a structure containing fields
  \param index Index position of the field within the structure
  \param data String value to be copied into the field
  \return nothing
*/
#define ast_string_field_index_set(x, index, data) do { \
    char *__zz__ = (char*) (x)->__begin_field[index]; \
    char *__data__ = (char*) (data); \
    size_t __dlen__ = strlen(__data__) + 1; \
    if ( __dlen__ == 1 ) {\
      (x)->__begin_field[index] = __ast_string_field_empty; \
    } else { \
      if ((__zz__[0] != 0) && (__dlen__ <= (strlen(__zz__) + 1))) { \
        memcpy(__zz__, __data__, __dlen__); \
      } else { \
        if (((x)->__begin_field[index] = __ast_string_field_alloc_space(&(x)->__field_mgr, __dlen__, &(x)->__begin_field[0], ast_string_field_count(x)))) { \
          char *__yy__ = (char *) (x)->__begin_field[index]; \
          memcpy(__yy__, __data__, __dlen__); \
        } \
      } \
     } \
   } while (0)

#define ast_string_field_index_logset(x, index, data, logstr) do { \
    char *__zz__ = (char*) (x)->__begin_field[index]; \
    char *__data__ = (char*) (data); \
    size_t __dlen__ = strlen(__data__) + 1; \
    if ( __dlen__ == 1 ) {\
      (x)->__begin_field[index] = __ast_string_field_empty; \
    } else { \
      if ((__zz__[0] != 0) && (__dlen__ <= strlen(__zz__) + 1)) { \
        ast_verbose("%s: ======replacing '%s' with '%s'\n", logstr, __zz__, __data__); \
        memcpy(__zz__, __data__, __dlen__); \
      } else { \
        if (((x)->__begin_field[index] = __ast_string_field_alloc_space(&(x)->__field_mgr, __dlen__, &(x)->__begin_field[0], ast_string_field_count(x)))) \
          ast_verbose("%s: ++++++allocating room for '%s' to replace '%s'\n", logstr, __data__, __zz__); \
          memcpy((char*) (x)->__begin_field[index], __data__, __dlen__); \
      } \
     } \
   } while (0)

/*!
  \brief Set a field to a simple string value
  \param x Pointer to a structure containing fields
  \param field Name of the field to set
  \param data String value to be copied into the field
  \return nothing
*/
#define ast_string_field_set(x, field, data) \
	ast_string_field_index_set(x, ast_string_field_index(x, field), data)

#define ast_string_field_logset(x, field, data, logstr) \
	ast_string_field_index_logset(x, ast_string_field_index(x, field), data, logstr)

/*!
  \brief Set a field to a complex (built) value
  \param x Pointer to a structure containing fields
  \param index Index position of the field within the structure
  \param fmt printf-style format string
  \param args Arguments for format string
  \return nothing
*/
#define ast_string_field_index_build(x, index, fmt, args...) \
	__ast_string_field_index_build(&(x)->__field_mgr, &(x)->__begin_field[0], ast_string_field_count(x), index, fmt, args)

/*!
  \brief Set a field to a complex (built) value with prebuilt va_lists.
  \param x Pointer to a structure containing fields
  \param index Index position of the field within the structure
  \param fmt printf-style format string
  \param args1 Arguments for format string in va_list format
  \param args2 a second copy of the va_list for the sake of bsd, with no va_list copy operation
  \return nothing
*/
#define ast_string_field_index_build_va(x, index, fmt, args1, args2) \
	__ast_string_field_index_build_va(&(x)->__field_mgr, &(x)->__begin_field[0], ast_string_field_count(x), index, fmt, args1, args2)

/*!
  \brief Set a field to a complex (built) value
  \param x Pointer to a structure containing fields
  \param field Name of the field to set
  \param fmt printf-style format string
  \param args Arguments for format string
  \return nothing
*/
#define ast_string_field_build(x, field, fmt, args...) \
	ast_string_field_index_build(x, ast_string_field_index(x, field), fmt, args)

/*!
  \brief Set a field to a complex (built) value
  \param x Pointer to a structure containing fields
  \param field Name of the field to set
  \param fmt printf-style format string
  \param argslist a va_list of the args
  \return nothing
*/
#define ast_string_field_build_va(x, field, fmt, args1, args2) \
	ast_string_field_index_build_va(x, ast_string_field_index(x, field), fmt, args1, args2)

/*!
  \brief Free a field's value.
  \param x Pointer to a structure containing fields
  \param index Index position of the field within the structure
  \return nothing

  \note Because of the storage pool used, the memory
  occupied by the field's value is \b not recovered; the field
  pointer is just changed to point to an empty string.
*/
#define ast_string_field_index_free(x, index) do { \
	(x)->__begin_field[index] = __ast_string_field_empty; \
	} while(0)

/*!
  \brief Free a field's value.
  \param x Pointer to a structure containing fields
  \param field Name of the field to free
  \return nothing

  \note Because of the storage pool used, the memory
  occupied by the field's value is \b not recovered; the field
  pointer is just changed to point to an empty string.
*/
#define ast_string_field_free(x, field) \
	ast_string_field_index_free(x, ast_string_field_index(x, field))

/*!
  \brief Free the stringfield storage pools attached to a structure
  \param x Pointer to a structure containing fields
  \return nothing

  After calling this macro, fields can no longer be accessed in
  structure; it should only be called immediately before freeing
  the structure itself.
*/
#define ast_string_field_free_memory(x) do { \
	struct ast_string_field_pool *this, *prev; \
	for (this = (x)->__field_mgr.pool; this; this = prev) { \
		prev = this->prev; \
		free(this); \
	} \
	} while(0)

/*!
  \brief Free the stringfields in a structure
  \param x Pointer to a structure containing fields
  \return nothing

  After calling this macro, the most recently allocated pool
  attached to the structure will be available for use by
  stringfields again.
*/
#define ast_string_field_reset_all(x) do { \
	int index; \
	for (index = 0; index < ast_string_field_count(x); index++) \
		ast_string_field_index_free(x, index); \
	(x)->__field_mgr.used = 0; \
	(x)->__field_mgr.space = (x)->__field_mgr.size; \
	} while(0)

#endif /* _ASTERISK_STRINGFIELDS_H */
