view waaad/dictionary.c @ 395:f0ba4fa1665e

Fixed couple of possible issues
author Sebastien Decugis <sdecugis@nict.go.jp>
date Mon, 01 Jun 2009 18:20:48 +0900
parents e86dba02630a
children 316bb3f38d04
line wrap: on
line source

/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
*													 *
* Copyright (c) 2009, WIDE Project and NICT								 *
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the WIDE Project or NICT nor the 						 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of WIDE Project and 						 *
*   NICT.												 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/

/* Dictionary facility.
 *
 * Handle the dictionary, that contains definitions for all Diameter objects supported by the application.
 *
 * See dictionary-api.h and dictionary.h for more information on the functions 
 *
 */

/* Note on possible optimization: we could use binary trees instead of ordered lists to make researchs faster.
 * Thanks to Jan Engelhardt from Circum project for the tip. */

#include "waaad-internal.h"

#include "dict-base.h"

/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Types and global objects                                           */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Names of the base types */
char * type_base_name[] = { /* must keep in sync with dict_base_type_t */
	"GROUPED", 	/* AVP_TYPE_GROUPED */
	"OCTETSTRING", 	/* AVP_TYPE_OCTETSTRING */
	"INTEGER32", 	/* AVP_TYPE_INTEGER32 */
	"INTEGER64", 	/* AVP_TYPE_INTEGER64 */
	"UNSIGNED32", 	/* AVP_TYPE_UNSIGNED32 */
	"UNSIGNED64", 	/* AVP_TYPE_UNSIGNED64 */
	"FLOAT32", 	/* AVP_TYPE_FLOAT32 */
	"FLOAT64"	/* AVP_TYPE_FLOAT64 */
	};

/* The read-write lock for the whole dictionary */
static pthread_rwlock_t dict_lock;

/* The number of lists in an object */
#define NB_LISTS_PER_OBJ	3

/* This is the internal description of a dictionary object */
typedef struct __dict_object {
	dict_object_type_t	type;	/* What type of object is this? */
	int			objeyec;/* eyecatcher for this object */
	int			typeyec;/* eyecatcher for this type of object */
	
	union {
		dict_vendor_data_t	vendor;
		dict_application_data_t	application;
		dict_type_data_t	type;
		dict_type_enum_data_t	type_enum;
		dict_avp_data_t		avp;
		dict_cmd_data_t		cmd;
		dict_rule_data_t	rule;
	} data;				/* The data of this object */
	
	struct __dict_object *	parent; /* The parent of this object, if any */
	
	uti_list_t		list[NB_LISTS_PER_OBJ];/* used to chain objects.*/
	/* More information about the lists :
	
	 - the use for each list depends on the type of object. See detail bellow.
	 
	 - a sentinel for a list does not have its 'o' field set. (this is the criteria to detect end of a loop)
	 
	 - The lists are always ordered. The criteria are described bellow. the functions to order them are referenced in dict_obj_info
	 
	 - The dict_lock must be held for any list operation.
	 
	 => VENDORS:
	 list[0]: list of the vendors, ordered by their id. The sentinel is g_dict_vendors (vendor with id 0)
	 list[1]: sentinel for the list of AVPs from this vendor, ordered by AVP code.
	 list[2]: sentinel for the list of AVPs from this vendor, ordered by AVP name.
	 
	 => APPLICATIONS:
	 list[0]: list of the applications, ordered by their id. The sentinel is g_dict_applications (application with id 0)
	 list[1]: not used
	 list[2]: not used.
	 
	 => TYPES:
	 list[0]: list of the types, ordered by their names. The sentinel is g_list_types.
	 list[1]: sentinel for the type_enum list of this type, ordered by their constant name.
	 list[2]: sentinel for the type_enum list of this type, ordered by their constant value.
	 
	 => TYPE_ENUMS:
	 list[0]: list of the contants for a given type, ordered by the constant name. Sentinel is a (list[1]) element of a TYPE object.
	 list[1]: list of the contants for a given type, ordered by the constant value. Sentinel is a (list[2]) element of a TYPE object.
	 list[2]: not used
	 
	 => AVPS:
	 list[0]: list of the AVP from a given vendor, ordered by avp code. Sentinel is a list[1] element of a VENDOR object.
	 list[1]: list of the AVP from a given vendor, ordered by avp name. Sentinel is a list[2] element of a VENDOR object.
	 list[2]: sentinel for the rule list that apply to this AVP.
	 
	 => COMMANDS:
	 list[0]: list of the commands, ordered by their names. The sentinel is g_list_cmd_name.
	 list[1]: list of the commands, ordered by their command code and 'R' flag. The sentinel is g_list_cmd_code.
	 list[2]: sentinel for the rule list that apply to this command.
	 
	 => RULES:
	 list[0]: list of the rules for a given (grouped) AVP or Command, ordered by the AVP name to which they refer. sentinel is list[2] of a command or (grouped) avp.
	 list[1]: not used
	 list[2]: not used.
	 
	 */
	 
	 /* Sentinel for the dispatch callbacks */
	 uti_list_t		disp_cbs;
	
} _dict_object_t;

/* Cast macro */
#define _O( object ) ((_dict_object_t *) (object))

#define OBJECT_EYECATCHER	(0x0b13c7)

/* Sentinels for the different lists */
static _dict_object_t	g_dict_vendors;	
static _dict_object_t	g_dict_applications;
static uti_list_t	g_list_types;
static uti_list_t	g_list_cmd_name;
static uti_list_t	g_list_cmd_code;

/* Special command object for answers with the 'E' bit set */
static _dict_object_t   g_dict_cmd_error;

/* Prototype of functions to dump the data structure */
typedef void (*dump_data_t) 	  ( void * data );

/* Forward declarations of such functions */
static void dump_vendor_data 	  ( void * data );
static void dump_application_data ( void * data );
static void dump_type_data 	  ( void * data );
  /* the dump function for enum has a different prototype since it need the datatype */
static void dump_avp_data 	  ( void * data );
static void dump_command_data 	  ( void * data );
static void dump_rule_data 	  ( void * data );

/* Prototype of functions to search an element in the dictionary */
typedef int (*search_fct_t) 	( int criteria, void * what, _dict_object_t **result );

/* Forward declarations of such functions */
static int search_vendor 	( int criteria, void * what, _dict_object_t **result );
static int search_application   ( int criteria, void * what, _dict_object_t **result );
static int search_type 		( int criteria, void * what, _dict_object_t **result );
static int search_type_enum 	( int criteria, void * what, _dict_object_t **result );
static int search_avp		( int criteria, void * what, _dict_object_t **result );
static int search_cmd		( int criteria, void * what, _dict_object_t **result );
static int search_rule		( int criteria, void * what, _dict_object_t **result );

/* The following array contains lot of data about the different types of objects, for automated handling */
static struct _dict_obj_info {
	dict_object_type_t 	type; 		/* information for this type */
	char *			name;		/* string describing this object, for debug */
	size_t			datasize;	/* The size of the data structure */
	int			parent;		/* 0: never; 1: may; 2: must */
	dict_object_type_t	parenttype;	/* The type of the parent, when relevant */
	int			eyecatcher;	/* A kind of signature for this object */
	dump_data_t		dump_data;	/* The function to dump the data section */
	search_fct_t		search_fct;	/* The function to search an object of this type */
	int			haslist[NB_LISTS_PER_OBJ];	/* Tell if this list is used */
	int			count;		/* The number of objects of this type in the dictionary */
} dict_obj_info[] = { { 0, "(error)", 0, 0, 0, 0, NULL, NULL, {0, 0, 0}, 0 }

	/* type			 name		datasize		     parent  	parenttype 
			eyecatcher		dump_data	  	search_fct,		haslist[] 	count*/

	,{ DICT_VENDOR,		"VENDOR",	sizeof(dict_vendor_data_t),	0, 	0,
			OBJECT_EYECATCHER + 1, 	dump_vendor_data, 	search_vendor, 		{ 1, 0, 0 },	0 }
	
	,{ DICT_APPLICATION,	"APPLICATION",	sizeof(dict_application_data_t),1, 	DICT_VENDOR,
			OBJECT_EYECATCHER + 2,	dump_application_data,	search_application,	{ 1, 0, 0 },	0 }
	
	,{ DICT_TYPE,		"TYPE",		sizeof(dict_type_data_t),	1, 	DICT_APPLICATION,
			OBJECT_EYECATCHER + 3,	dump_type_data,		search_type,		{ 1, 0, 0 },	0 }
	
	,{ DICT_TYPE_ENUM,	"TYPE_ENUM",	sizeof(dict_type_enum_data_t),	2, 	DICT_TYPE,
			OBJECT_EYECATCHER + 4,	NULL,			search_type_enum,	{ 1, 1, 0 },	0 }
	
	,{ DICT_AVP,		"AVP",		sizeof(dict_avp_data_t),	1, 	DICT_TYPE,
			OBJECT_EYECATCHER + 5,	dump_avp_data,		search_avp,		{ 1, 1,	0 },	0 }
	
	,{ DICT_COMMAND,	"COMMAND",	sizeof(dict_cmd_data_t),	1, 	DICT_APPLICATION,
			OBJECT_EYECATCHER + 6,	dump_command_data,	search_cmd,		{ 1, 1, 0 },	0 }
	
	,{ DICT_RULE,		"RULE",		sizeof(dict_rule_data_t),	2, 	-1 /* special case: grouped avp or command */,
			OBJECT_EYECATCHER + 7,	dump_rule_data,		search_rule,		{ 1, 0, 0 },	0 }
	
};
	
/* Macro to verify a "type" value */
#define CHECK_TYPE( type ) ( ((type) > 0) && ((type) <= DICT_TYPE_MAX) )

/* Get information line for a given object */
#define _OBINFO(object) (dict_obj_info[CHECK_TYPE(_O(object)->type) ? _O(object)->type : 0])


/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Objects management                                                 */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Functions to manage the objects creation and destruction. */


/* Initialize an object */
static void init_object( _dict_object_t * obj, dict_object_type_t type )
{
	int i;
	
	TRACE_ENTRY("%p %d", obj, type);
	
	/* Clean the object first */
	memset ( obj, 0, sizeof(_dict_object_t));
	
	CHECK_PARAMS_DO(  CHECK_TYPE(type),  return  );

	obj->type = type;
	obj->objeyec = OBJECT_EYECATCHER;
	obj->typeyec = _OBINFO(obj).eyecatcher;

	/* We don't initialize the data nor the parent here */
	
	/* Now init the lists */
	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
		if (_OBINFO(obj).haslist[i] != 0) 
			uti_list_init(&obj->list[i], obj);
		else
			uti_list_init(&obj->list[i], NULL);
	}
	
	uti_list_init(&obj->disp_cbs, NULL);
}

#define DUP_string( str ) {				\
	char * __str = (str);				\
	CHECK_MALLOC( (str) = strdup(__str) );		\
}		

/* Initialize the "data" part of an object */
int init_object_data(void * dest, void * source, dict_object_type_t type)
{
	TRACE_ENTRY("%p %p %d", dest, source, type);
	
	/* Generic: copy the full data structure */	
	memcpy( dest, source, dict_obj_info[type].datasize );
	
	/* Then strings must be duplicated, not copied */
	/* This function might be simplified by always defining the "name" field as the first field of the structures, but... it's error-prone */
	switch (type) {
		case DICT_VENDOR:
			DUP_string( ((dict_vendor_data_t *)dest)->vendor_name );
			break;
		
		case DICT_APPLICATION:
			DUP_string( ((dict_application_data_t *)dest)->application_name );
			break;
			
		case DICT_TYPE:
			DUP_string( ((dict_type_data_t *)dest)->type_name );
			break;
			
		case DICT_TYPE_ENUM:
			DUP_string( ((dict_type_enum_data_t *)dest)->enum_name );
			break;

		case DICT_AVP:
			DUP_string( ((dict_avp_data_t *)dest)->avp_name );
			break;
			
		case DICT_COMMAND:
			DUP_string( ((dict_cmd_data_t *)dest)->cmd_name );
			break;
		
		default:
			/* Nothing to do */
			;
	}
	
	return 0;
}


/* Check that an object is valid (1: OK, 0: error) */
static int verify_object( _dict_object_t * obj )
{
	TRACE_ENTRY("%p", obj);
	
	CHECK_PARAMS_DO(  obj
			&& (obj->objeyec == OBJECT_EYECATCHER)
			&& CHECK_TYPE(obj->type)
			&& (obj->typeyec == dict_obj_info[obj->type].eyecatcher),
		{
			if (obj) {
				TRACE_DEBUG(FULL, "Invalid object : %p\n"
						  "     obj->objeyec : %x / %x\n"
						  "     obj->type    : %d\n"
						  "     obj->objeyec : %x / %x\n"
						  "     obj->typeyec : %x / %x", 
						obj,
						obj->objeyec, OBJECT_EYECATCHER,
						obj->type,
						obj->objeyec, OBJECT_EYECATCHER,
						obj->typeyec, _OBINFO(obj).eyecatcher);
			}
			return 0;
		}  );
	
	/* The object is probably valid. */
	return 1;
}

/* Free the data associated to an object */
static void destroy_object_data(_dict_object_t * obj)
{
	/* TRACE_ENTRY("%p", obj); */
	
	switch (obj->type) {
		case DICT_VENDOR:
			free( obj->data.vendor.vendor_name );
			break;
		
		case DICT_APPLICATION:
			free( obj->data.application.application_name );
			break;
			
		case DICT_TYPE:
			free( obj->data.type.type_name );
			break;
			
		case DICT_TYPE_ENUM:
			free( obj->data.type_enum.enum_name );
			break;

		case DICT_AVP:
			free( obj->data.avp.avp_name );
			break;
			
		case DICT_COMMAND:
			free( obj->data.cmd.cmd_name );
			break;
		
		default:
			/* nothing to do */
			;
	}
}

/* Forward declaration */
static void destroy_object(_dict_object_t * obj);

/* Destroy all objects in a list - the lock must be held */
static void destroy_list(uti_list_t * head) 
{
	/* TRACE_ENTRY("%p", head); */
	
	/* loop in the list */
	while (!IS_LIST_EMPTY(head))
	{
		/* When destroying the object, it is unlinked from the list */
		destroy_object(_O(head->next->o));
	}
}
	
/* Free an object and its sublists */
static void destroy_object(_dict_object_t * obj)
{
	int i;
	
	/* TRACE_ENTRY("%p", obj); */
	
	/* Update global count */
	_OBINFO(obj).count--;
	
	/* Mark the object as invalid */
	obj->objeyec = 0xdead;
	
	/* First, destroy the data associated to the object */
	destroy_object_data(obj);
	
	for (i=0; i<NB_LISTS_PER_OBJ; i++) {
		if (_OBINFO(obj).haslist[i])
			/* unlink the element from the list */
			uti_list_unlink( &obj->list[i] );
		else
			/* This is either a sentinel or unused (=emtpy) list, let's destroy it */
			destroy_list( &obj->list[i] );
	}
	
	/* Unlink all elements from the dispatch list; they will be freed when callback is unregistered */
	while (!IS_LIST_EMPTY(&obj->disp_cbs)) {
		uti_list_unlink( obj->disp_cbs.next );
	}
	
	/* Last, destroy the object */
	free(obj);
}


/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Compare functions                                                  */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Functions that are used to ordering the lists. */


/* Compare two values */
#define order_scalar( i1, i2 ) \
	((i1 < i2 ) ? -1 : ( i1 > i2 ? 1 : 0 )) 

/* Compare two vendor objects by their id (checks already performed) */
static int order_vendor_by_id ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return order_scalar( o1->data.vendor.vendor_id, o2->data.vendor.vendor_id );
}

/* Compare two application objects by their id (checks already performed) */
static int order_appli_by_id  ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return order_scalar( o1->data.application.application_id, o2->data.application.application_id );
}

/* Compare two type objects by their name (checks already performed) */
static int order_type_by_name ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return strcmp( o1->data.type.type_name, o2->data.type.type_name );
}

/* Compare two type_enum objects by their names (checks already performed) */
static int order_enum_by_name ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return strcmp( o1->data.type_enum.enum_name, o2->data.type_enum.enum_name );
}

/* Compare two type_enum objects by their values (checks already performed) */
static int order_enum_by_val  ( _dict_object_t *o1, _dict_object_t *o2 )
{
	size_t oslen;
	int cmp = 0;
	
	TRACE_ENTRY("%p %p", o1, o2);
	
	/* The comparison function depends on the type of data */
	switch ( o1->parent->data.type.type_base ) {
		case AVP_TYPE_OCTETSTRING:
			oslen = o1->data.type_enum.enum_value.os.len;
			if (o2->data.type_enum.enum_value.os.len < oslen)
				oslen = o2->data.type_enum.enum_value.os.len;
			cmp = memcmp(o1->data.type_enum.enum_value.os.data, o2->data.type_enum.enum_value.os.data, oslen );
			return (cmp ? cmp : order_scalar(o1->data.type_enum.enum_value.os.len,o2->data.type_enum.enum_value.os.len));
		
		case AVP_TYPE_INTEGER32:
			return order_scalar( o1->data.type_enum.enum_value.i32, o2->data.type_enum.enum_value.i32 );

		case AVP_TYPE_INTEGER64:
			return order_scalar( o1->data.type_enum.enum_value.i64, o2->data.type_enum.enum_value.i64 );

		case AVP_TYPE_UNSIGNED32:
			return order_scalar( o1->data.type_enum.enum_value.u32, o2->data.type_enum.enum_value.u32 );

		case AVP_TYPE_UNSIGNED64:
			return order_scalar( o1->data.type_enum.enum_value.u64, o2->data.type_enum.enum_value.u64 );

		case AVP_TYPE_FLOAT32:
			return order_scalar( o1->data.type_enum.enum_value.f32, o2->data.type_enum.enum_value.f32 );

		case AVP_TYPE_FLOAT64:
			return order_scalar( o1->data.type_enum.enum_value.f64, o2->data.type_enum.enum_value.f64 );

		case AVP_TYPE_GROUPED:
		default:
			ASSERT(0);
	}
	return 0;
}

/* Compare two avp objects by their codes (checks already performed) */
static int order_avp_by_code  ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return order_scalar( o1->data.avp.avp_code, o2->data.avp.avp_code );
}

/* Compare two avp objects by their names (checks already performed) */
static int order_avp_by_name  ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return strcmp( o1->data.avp.avp_name, o2->data.avp.avp_name );
}

/* Compare two command objects by their names (checks already performed) */
static int order_cmd_by_name  ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return strcmp( o1->data.cmd.cmd_name, o2->data.cmd.cmd_name );
}

/* Compare two command objects by their codes and flags (request or answer) (checks already performed) */
static int order_cmd_by_codefl( _dict_object_t *o1, _dict_object_t *o2 )
{
	uint8_t fl1, fl2;
	int cmp = 0;
	
	TRACE_ENTRY("%p %p", o1, o2);
	
	cmp = order_scalar( o1->data.cmd.cmd_code, o2->data.cmd.cmd_code );
	if (cmp) 
		return cmp;
	
	/* Same command code, we must compare the value of the 'R' flag */
	fl1 = o1->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
	fl2 = o2->data.cmd.cmd_flag_val & CMD_FLAG_REQUEST;
	
	/* We want requests first, so we reverse the operators here */
	return order_scalar(fl2, fl1);
		
}

/* Compare two rule object by the AVP name that they refer (checks already performed) */
static int order_rule_by_avpn ( _dict_object_t *o1, _dict_object_t *o2 )
{
	TRACE_ENTRY("%p %p", o1, o2);
	
	return strcmp( _O(o1->data.rule.rule_avp)->data.avp.avp_name, _O(o2->data.rule.rule_avp)->data.avp.avp_name );
}

/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Search  functions                                                  */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* Functions used to search for objects in the lists, according to some criteria */

/* On a general note, if result is not NULL, ENOENT is not returned but *result is NULL. */

/* The following macros assume that "what", "ret", "result" (variables), and "end" (label) exist
in the local context where they are called. They are meant to be called only from the functions that follow. */

/* For searchs of type "xxx_OF_xxx": children's parent or default parent */
#define SEARCH_childs_parent( type_of_child, default_parent ) {			\
	_dict_object_t *__child = (_dict_object_t *) what;			\
	CHECK_PARAMS_DO( verify_object(__child) && 				\
		(__child->type == (type_of_child)), 				\
		   {  ret = EINVAL; goto end;  }  );				\
	ret = 0;								\
	if (result)								\
		*result = (__child->parent ? __child->parent :(default_parent));\
}

/* For search of strings in lists. isindex= 1 if the string is the ordering key of the list */
#define SEARCH_string( str, sentinel, datafield, isindex ) {			\
	char * __str = (char *) str;						\
	int __cmp;								\
	uti_list_t * __li;							\
	ret = 0;								\
	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
		__cmp = strcmp(__str, _O(__li->next->o)->data. datafield );	\
		if (__cmp == 0) {						\
			if (result)						\
				*result = _O(__li->next->o);			\
			goto end;						\
		}								\
		if ((isindex) && (__cmp < 0))					\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}
/* For search of octetstrings in lists (not \0 terminated). */
#define SEARCH_ocstring( ostr, length, sentinel, osdatafield, isindex ) {	\
	unsigned char * __ostr = (unsigned char *) ostr;			\
	int __cmp;								\
	size_t __len;								\
	uti_list_t * __li;							\
	ret = 0;								\
	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
		__len = _O(__li->next->o)->data. osdatafield .len;		\
		if ( __len > (length) )						\
			__len = (length);					\
		__cmp = memcmp(__ostr, 						\
				_O(__li->next->o)->data. osdatafield .data, 	\
				__len);						\
		if (! __cmp) {							\
			__cmp = order_scalar( length,				\
				_O(__li->next->o)->data. osdatafield .len); 	\
		}								\
		if (__cmp == 0) {						\
			if (result)						\
				*result = _O(__li->next->o);			\
			goto end;						\
		}								\
		if ((isindex) && (__cmp < 0))					\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}
/* For search of AVP name in rule lists. */
#define SEARCH_ruleavpname( str, sentinel ) {					\
	char * __str = (char *) str;						\
	int __cmp;								\
	uti_list_t * __li;							\
	ret = 0;								\
	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
		__cmp = strcmp(__str, 						\
		  _O(_O(__li->next->o)->data.rule.rule_avp)->data.avp.avp_name);\
		if (__cmp == 0) {						\
			if (result)						\
				*result = _O(__li->next->o);			\
			goto end;						\
		}								\
		if (__cmp < 0)							\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

/* For search of scalars in lists. isindex= 1 if the value is the ordering key of the list */
#define SEARCH_scalar( value, sentinel, datafield, isindex, defaultobj ) {	\
	int __cmp;								\
	uti_list_t * __li;							\
	ret = 0;								\
	if (  ((defaultobj) != NULL) 						\
		   && (_O(defaultobj)->data. datafield  == value)) {		\
		if (result)							\
			*result = _O(defaultobj);				\
		goto end;							\
	}									\
	for  (__li = (sentinel); __li->next != (sentinel); __li = __li->next) {	\
		__cmp= order_scalar(value, _O(__li->next->o)->data. datafield );\
		if (__cmp == 0) {						\
			if (result)						\
				*result = _O(__li->next->o);			\
			goto end;						\
		}								\
		if ((isindex) && (__cmp < 0))					\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}

/* For search of commands in lists by code and flag. R_flag_val = 0 or CMD_FLAG_REQUEST */
#define SEARCH_codefl( value, R_flag_val) {					\
	int __cmp;								\
	uti_list_t * __li;							\
	ret = 0;								\
	for  (	  __li = &g_list_cmd_code; 					\
		  __li->next != &g_list_cmd_code; 				\
		  __li = __li->next) {						\
		__cmp = order_scalar(value, 					\
				_O(__li->next->o)->data.cmd.cmd_code );		\
		if (__cmp == 0) {						\
			uint8_t __mask, __val;					\
			__mask = _O(__li->next->o)->data.cmd.cmd_flag_mask;	\
			__val  = _O(__li->next->o)->data.cmd.cmd_flag_val;	\
			if ( ! (__mask & CMD_FLAG_REQUEST) )			\
				continue;					\
			if ( ( __val & CMD_FLAG_REQUEST ) != R_flag_val )	\
				continue;					\
			if (result)						\
				*result = _O(__li->next->o);			\
			goto end;						\
		}								\
		if (__cmp < 0)							\
			break;							\
	}									\
	if (result)								\
		*result = NULL;							\
	else									\
		ret = ENOENT;							\
}


static int search_vendor ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	vendor_id_t id;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case VENDOR_BY_ID_REF:
		case VENDOR_BY_ID_VAL:
			if (criteria == VENDOR_BY_ID_REF) {
				id = *(vendor_id_t *) what;
			} else {
				ASSERT(sizeof(vendor_id_t) <= sizeof(void *));  /* otherwise the cast does not work */
				id = (vendor_id_t) (unsigned long) what;
			}

			SEARCH_scalar( id, &g_dict_vendors.list[0], vendor.vendor_id, 1, &g_dict_vendors );
			break;
				
		case VENDOR_BY_NAME:
			/* "what" is a vendor name */
			SEARCH_string( what, &g_dict_vendors.list[0], vendor.vendor_name, 0);
			break;
			
		case VENDOR_OF_APPLICATION:
			/* "what" should be an application object */
			SEARCH_childs_parent( DICT_APPLICATION, &g_dict_vendors );
			break;
		
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_application ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	application_id_t id;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case APPLICATION_BY_ID_REF:
		case APPLICATION_BY_ID_VAL:
			if (criteria == APPLICATION_BY_ID_REF) {
				id = *(application_id_t *) what;
			} else {
				ASSERT(sizeof(application_id_t) <= sizeof(void *));  /* otherwise the cast does not work */
				id = (application_id_t) (unsigned long) what;
			}

			SEARCH_scalar( id, &g_dict_applications.list[0],  application.application_id, 1, &g_dict_applications );
			break;
				
		case APPLICATION_BY_NAME:
			/* "what" is an application name */
			SEARCH_string( what, &g_dict_applications.list[0], application.application_name, 0);
			break;
			
		case APPLICATION_OF_TYPE:
			/* "what" should be a type object */
			SEARCH_childs_parent( DICT_TYPE, &g_dict_applications );
			break;
		
		case APPLICATION_OF_COMMAND:
			/* "what" should be a command object */
			SEARCH_childs_parent( DICT_COMMAND, &g_dict_applications );
			break;
		
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_type ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case TYPE_BY_NAME:
			/* "what" is a type name */
			SEARCH_string( what, &g_list_types, type.type_name, 1);
			break;
			
		case TYPE_OF_ENUM:
			/* "what" should be a type_enum object */
			SEARCH_childs_parent( DICT_TYPE_ENUM, NULL );
			break;
		
		case TYPE_OF_AVP:
			/* "what" should be an avp object */
			SEARCH_childs_parent( DICT_AVP, NULL );
			break;
		
				
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_type_enum ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case ENUM_BY_STRUCT:
			{
				_dict_object_t * parent = NULL;
				dict_type_enum_request_t * _what = (dict_type_enum_request_t *) what;
				
				CHECK_PARAMS(  _what  &&  ( _what->type_obj || _what->type_name )  );
				
				if (_what->type_obj != NULL) {
					parent = _O(_what->type_obj);
					CHECK_PARAMS(  verify_object(parent)  &&  (parent->type == DICT_TYPE)  );
				} else {
					/* We received only the type name, we must find it first */
					CHECK_FCT_DO( search_type( TYPE_BY_NAME, _what->type_name, &parent ),
							CHECK_PARAMS( 0 ) );
				}
				
				/* From here the "parent" object is valid */
				
				if ( _what->search.enum_name != NULL ) {
					/* We are looking for this string */
					SEARCH_string(  _what->search.enum_name, &parent->list[1], type_enum.enum_name, 1 );
				} else {
					/* We are looking for the value in enum_value */
					switch (parent->data.type.type_base) {
						case AVP_TYPE_OCTETSTRING:
							SEARCH_ocstring( _what->search.enum_value.os.data, 
									 _what->search.enum_value.os.len, 
									 &parent->list[2], 
									 type_enum.enum_value.os , 
									 1 );
							break;

						case AVP_TYPE_INTEGER32:
							SEARCH_scalar(	_what->search.enum_value.i32,
									&parent->list[2],
									type_enum.enum_value.i32,
									1,
									(_dict_object_t *)NULL);
							break;
							
						case AVP_TYPE_INTEGER64:
							SEARCH_scalar(	_what->search.enum_value.i64,
									&parent->list[2],
									type_enum.enum_value.i64,
									1,
									(_dict_object_t *)NULL);
							break;
							
						case AVP_TYPE_UNSIGNED32:
							SEARCH_scalar(	_what->search.enum_value.u32,
									&parent->list[2],
									type_enum.enum_value.u32,
									1,
									(_dict_object_t *)NULL);
							break;
							
						case AVP_TYPE_UNSIGNED64:
							SEARCH_scalar(	_what->search.enum_value.u64,
									&parent->list[2],
									type_enum.enum_value.u64,
									1,
									(_dict_object_t *)NULL);
							break;
							
						case AVP_TYPE_FLOAT32:
							SEARCH_scalar(	_what->search.enum_value.f32,
									&parent->list[2],
									type_enum.enum_value.f32,
									1,
									(_dict_object_t *)NULL);
							break;
							
						case AVP_TYPE_FLOAT64:
							SEARCH_scalar(	_what->search.enum_value.f64,
									&parent->list[2],
									type_enum.enum_value.f64,
									1,
									(_dict_object_t *)NULL);
							break;
							
						default:
							/* Invalid parent type basetype */
							CHECK_PARAMS( parent = NULL );
					}
				}
				
			}
			break;
		
				
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_avp ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case AVP_BY_CODE_REF:
		case AVP_BY_CODE_VAL:
			{
				avp_code_t code;
				if (criteria == AVP_BY_CODE_REF) {
					code = *(avp_code_t *) what;
				} else {
					ASSERT(sizeof(avp_code_t) <= sizeof(void *));  /* otherwise the cast does not work */
					code = (avp_code_t) (unsigned long) what;
				}

				SEARCH_scalar( code, &g_dict_vendors.list[1],  avp.avp_code, 1, (_dict_object_t *)NULL );
			}
			break;
				
		case AVP_BY_NAME:
			/* "what" is the AVP name, vendor 0 */
			SEARCH_string( what, &g_dict_vendors.list[2], avp.avp_name, 1);
			break;
			
		case AVP_BY_CODE_AND_VENDOR:
		case AVP_BY_NAME_AND_VENDOR:
			{
				dict_avp_request_t * _what = (dict_avp_request_t *) what;
				_dict_object_t * vendor = NULL;
				
				CHECK_PARAMS( (criteria != AVP_BY_NAME_AND_VENDOR) || _what->avp_name  );
				
				/* Now look for the vendor first */
				CHECK_FCT( search_vendor( VENDOR_BY_ID_REF, &_what->avp_vendor, &vendor ) );
				if (vendor == NULL) {
					if (result)
						*result = NULL;
					else
						ret = ENOENT;
					goto end;
				}
				
				/* We now have our vendor = head of the appropriate avp list */
				if (criteria == AVP_BY_NAME_AND_VENDOR) {
					SEARCH_string( _what->avp_name, &vendor->list[2], avp.avp_name, 1);
				} else {
					/* AVP_BY_CODE_AND_VENDOR */
					SEARCH_scalar( _what->avp_code, &vendor->list[1],  avp.avp_code, 1, (_dict_object_t *)NULL );
				}
			}
			break;
		
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_cmd ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case CMD_BY_NAME:
			/* "what" is a command name */
			SEARCH_string( what, &g_list_cmd_name, cmd.cmd_name, 1);
			break;
			
		case CMD_BY_CODE_R_VAL:
		case CMD_BY_CODE_A_VAL:
		case CMD_BY_CODE_R_REF:
		case CMD_BY_CODE_A_REF:
			{
				command_code_t code;
				uint8_t searchfl = 0;
				
				/* The command code that we are searching */
				if ( (criteria == CMD_BY_CODE_R_REF) || (criteria == CMD_BY_CODE_A_REF)) {
					code = *(command_code_t *) what;
				} else {
					ASSERT(sizeof(command_code_t) <= sizeof(void *));  /* otherwise the cast does not work */
					code = (command_code_t) (unsigned long) what;
				}
				
				/* The flag (request or answer) of the command we are searching */
				if ( (criteria == CMD_BY_CODE_R_VAL) || (criteria == CMD_BY_CODE_R_REF)) {
					searchfl = CMD_FLAG_REQUEST;
				}
				
				/* perform the search */
				SEARCH_codefl( code, searchfl );
			}
			break;
				
		case CMD_ANSWER:
			{
				/* "what" is a command object of type "request" */
				_dict_object_t * req = (_dict_object_t *) what;
				_dict_object_t * ans = NULL;
				
				CHECK_PARAMS( verify_object(req) 
						&& (req->type == DICT_COMMAND)
						&& (req->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST)
						&& (req->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST) );
				
				/* The answer is supposed to be the next element in the list, if it exists */
				ans = req->list[1].next->o;
				if ( ans == NULL ) {
					TRACE_DEBUG( FULL, "the request was the last element in the list" );
					ret = ENOENT;
					goto end;
				}
				
				/* Now check that the ans element is really the correct one */
				if (  (ans->data.cmd.cmd_code != req->data.cmd.cmd_code)
				   || (!(ans->data.cmd.cmd_flag_mask & CMD_FLAG_REQUEST))
				   || (  ans->data.cmd.cmd_flag_val  & CMD_FLAG_REQUEST ) ) {
					TRACE_DEBUG( FULL, "the answer does not follow the request in the list" );
					ret = ENOENT;
					goto end;
				}
				
				if (result)
					*result = ans;
				ret = 0;
			}						
			break;
			
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}

static int search_rule ( int criteria, void * what, _dict_object_t **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%d %p %p", criteria, what, result);
	
	switch (criteria) {
		case RULE_BY_AVP_AND_PARENT:
			{
				_dict_object_t * parent = NULL;
				_dict_object_t * avp = NULL;
				dict_rule_request_t * _what = (dict_rule_request_t *) what;
				
				CHECK_PARAMS( _what 
						&& (parent = _O(_what->rule_parent))
						&& (avp    = _O(_what->rule_avp   )) );
				
				CHECK_PARAMS( verify_object(parent) 
						&& ((parent->type == DICT_COMMAND) 
						 || ((parent->type == DICT_AVP) && (parent->data.avp.avp_basetype == AVP_TYPE_GROUPED))) );
				
				CHECK_PARAMS( verify_object(avp) && (avp->type == DICT_AVP) );
				
				/* Perform the search */
				SEARCH_ruleavpname( avp->data.avp.avp_name, &parent->list[2]);
				
			}
			break;
			
		default:
			/* Invalid criteria */
			CHECK_PARAMS( criteria = 0 );
	}
end:
	return ret;
}


/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  Dump / debug functions                                             */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* The following functions are used to debug the module, and allow to print out the content of the dictionary */
static void dump_vendor_data ( void * data )
{
	dict_vendor_data_t * vendor = (dict_vendor_data_t *)data;
	
	log_debug("data: %-6u \"%s\"", vendor->vendor_id, vendor->vendor_name);
}
static void dump_application_data ( void * data )
{
	dict_application_data_t * appli = (dict_application_data_t *) data;
	log_debug("data: %-6u \"%s\"", appli->application_id, appli->application_name);
}
static void dump_type_data ( void * data )
{
	dict_type_data_t * type = ( dict_type_data_t * ) data;
	
	log_debug("data: %-12s \"%s\"", 
			type_base_name[type->type_base], 
			type->type_name);
}
static void dump_type_enum_data ( dict_type_enum_data_t * type_enum, dict_base_type_t type )
{
	const int LEN_MAX = 20;
	log_debug("data: (%-12s) \"%s\" -> ", type_base_name[type], type_enum->enum_name);
	switch (type) {
		case AVP_TYPE_OCTETSTRING:
			{
				int i, n=LEN_MAX;
				if (type_enum->enum_value.os.len < LEN_MAX)
					n = type_enum->enum_value.os.len;
				for (i=0; i < n; i++)
					log_debug("0x%02.2X/'%c' ", type_enum->enum_value.os.data[i], type_enum->enum_value.os.data[i]);
				if (n == LEN_MAX)
					log_debug("...");
			}
			break;
		
		case AVP_TYPE_INTEGER32:
			log_debug("%i", type_enum->enum_value.i32);
			break;

		case AVP_TYPE_INTEGER64:
			log_debug("%lli", type_enum->enum_value.i64);
			break;

		case AVP_TYPE_UNSIGNED32:
			log_debug("%u", type_enum->enum_value.u32);
			break;

		case AVP_TYPE_UNSIGNED64:
			log_debug("%llu", type_enum->enum_value.u64);
			break;

		case AVP_TYPE_FLOAT32:
			log_debug("%f", type_enum->enum_value.f32);
			break;

		case AVP_TYPE_FLOAT64:
			log_debug("%g", type_enum->enum_value.f64);
			break;
		
		default:
			log_debug("??? (ERROR unknown type %d)", type);
	}
}
static void dump_avp_data ( void * data )
{
	dict_avp_data_t * avp = (dict_avp_data_t * ) data;
	log_debug("data: v/m:" DUMP_AVPFL_str "/" DUMP_AVPFL_str ", %12s, %-6u \"%s\"", 
			DUMP_AVPFL_val(avp->avp_flag_val), 
			DUMP_AVPFL_val(avp->avp_flag_mask), 
			type_base_name[avp->avp_basetype], 
			avp->avp_code, 
			avp->avp_name );
}
static void dump_command_data ( void * data )
{
	dict_cmd_data_t * cmd = (dict_cmd_data_t *) data;
	log_debug("data: v/m:" DUMP_CMDFL_str "/" DUMP_CMDFL_str ", %-6u \"%s\"", 
			DUMP_CMDFL_val(cmd->cmd_flag_val), DUMP_CMDFL_val(cmd->cmd_flag_mask), cmd->cmd_code, cmd->cmd_name);
}
static void dump_rule_data ( void * data )
{
	dict_rule_data_t * rule = (dict_rule_data_t * )data;
	log_debug("data: pos:%d ord:%d m/M/t:%2d/%2d/%2d avp:\"%s\"", 
			rule->rule_position, 
			rule->rule_order, 
			rule->rule_min, 
			rule->rule_max,
			rule->rule_template, 
			_O(rule->rule_avp)->data.avp.avp_name);
}

static void dump_object ( _dict_object_t * obj, int parents, int depth, int indent );

static void dump_list ( uti_list_t * sentinel, int parents, int depth, int indent )
{
	uti_list_t * li = sentinel;
	/* We don't lock here, the caller must have taken the dictionary lock for reading already */
	while (li->next != sentinel)
	{
		li = li->next;
		dump_object( _O(li->o), parents, depth, indent );
	}
}

static void dump_object ( _dict_object_t * obj, int parents, int depth, int indent )
{
	if (obj == NULL)
		return;
	
	if (parents)
		dump_object (obj->parent, parents-1, 0, indent + 1 );
	
	log_debug("%*s@%p: %s%s (p:%-9p) ", 
			indent,
			"",
			obj, 
			verify_object(obj) ? "" : "NON VALID ", 
			_OBINFO(obj).name, 
			obj->parent);
	
	if (obj->type == DICT_TYPE_ENUM)
		dump_type_enum_data ( &obj->data.type_enum, _O(obj->parent)->data.type.type_base );
	else
		_OBINFO(obj).dump_data(&obj->data);
	
	log_debug("\n");
	
	if (depth) {
		int i;
		for (i=0; i<NB_LISTS_PER_OBJ; i++) {
			if ((obj->list[i].o == NULL) && (obj->list[i].next != &obj->list[i])) {
				log_debug("%*s>%p: list[%d]:\n", indent, "", obj, i);
				dump_list(&obj->list[i], parents, depth - 1, indent + 2);
			}
		}
	}
}

void dump_dictionary(void)
{
	int i;
	
	CHECK_POSIX_DO(  pthread_rwlock_rdlock( &dict_lock ), /* ignore */  );
	
	log_debug("######################################################\n");
	log_debug("###### Dumping vendors, AVPs and related rules #######\n");
	
	dump_object( &g_dict_vendors, 0, 3, 0 );
	
	log_debug("###### Dumping applications #######\n");

	dump_object( &g_dict_applications, 0, 1, 0 );
	
	log_debug("###### Dumping types #######\n");

	dump_list( &g_list_types, 0, 2, 0 );
	
	log_debug("###### Dumping commands per name #######\n");

	dump_list( &g_list_cmd_name, 0, 2, 0 );
	
	log_debug("###### Dumping commands per code and flags #######\n");

	dump_list( &g_list_cmd_code, 0, 0, 0 );
	
	log_debug("###### Statistics #######\n");

	for (i=1; i<=DICT_TYPE_MAX; i++)
		log_debug(" %5d objects of type %s\n", dict_obj_info[i].count, dict_obj_info[i].name);
	
	log_debug("######################################################\n");
	
	/* Free the rwlock */
	CHECK_POSIX_DO(  pthread_rwlock_unlock( &dict_lock ), /* ignore */  );
}

void dict_dump( dict_object_t * object, int parent, int depth)
{
	dump_object ( _O(object), parent, depth, 1 );
}


/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  The API functions                                                  */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/

/* These are the functions exported to the extensions, and the main entry points of the module. */

/* Get the type of an object */
int dict_gettype ( dict_object_t * object, dict_object_type_t * type)
{
	TRACE_ENTRY("%p %p", object, type);
	
	CHECK_PARAMS( type && verify_object(_O(object)) );
	
	/* Copy the type and return */
	*type = _O(object)->type;
	return 0;
}

/* Get the data associated to an object */
int dict_getval ( dict_object_t * object, void * val)
{
	TRACE_ENTRY("%p %p", object, val);
	
	CHECK_PARAMS( val && verify_object(_O(object)) );
	
	/* Copy the value and return */
	memcpy(val, &_O(object)->data, _OBINFO(object).datasize);;
	return 0;
}

/* Add a new object in the dictionary */
int dict_new ( dict_object_type_t type, void * data, dict_object_t * parent, dict_object_t **ref )
{
	int ret = 0;
	_dict_object_t * new = NULL;
	_dict_object_t * vendor = NULL;
	
	TRACE_ENTRY("%d(%s) %p %p %p", type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, data, parent, ref);
	
	/* Check parameters */
	CHECK_PARAMS( CHECK_TYPE(type) && data  );
	
	/* Check the "parent" parameter */
	switch (dict_obj_info[type].parent) {
		case 0:	/* parent is forbidden */
			CHECK_PARAMS( parent == NULL );
		
		case 1:	/* parent is optional */
			if (parent == NULL)
				break;
		
		case 2: /* parent is mandatory */
			CHECK_PARAMS(  verify_object(parent)  );
			
			if (type == DICT_RULE ) { /* Special case : grouped AVP or Command parents are allowed */
				CHECK_PARAMS( (_O(parent)->type == DICT_COMMAND ) 
						|| ( (_O(parent)->type == DICT_AVP) && (_O(parent)->data.avp.avp_basetype == AVP_TYPE_GROUPED ) ) );
			} else {
				CHECK_PARAMS( _O(parent)->type == dict_obj_info[type].parenttype );
			}
	}
	
	/* For AVP object, we must also check that the "vendor" referenced exists */
	if (type == DICT_AVP) {
		CHECK_FCT_DO(  dict_search( DICT_VENDOR, VENDOR_BY_ID_REF, &(((dict_avp_data_t *)data)->avp_vendor), (void*)&vendor ),
				CHECK_PARAMS( vendor = NULL )  );
		
		/* Also check if a parent is provided, that the type are the same */
		if (parent) {
			CHECK_PARAMS(  _O(parent)->data.type.type_base == ((dict_avp_data_t *)data)->avp_basetype  );
		}
	}
	
	/* For RULE object, we must also check that the "avp" referenced exists */
	if (type == DICT_RULE) {
		CHECK_PARAMS(  verify_object(((dict_rule_data_t *)data)->rule_avp)  );
		CHECK_PARAMS(  _O(((dict_rule_data_t *)data)->rule_avp)->type == DICT_AVP  );
	}
	
	/* For COMMAND object, check that the 'R' flag is fixed */
	if (type == DICT_COMMAND) {
		CHECK_PARAMS( ((dict_cmd_data_t *)data)->cmd_flag_mask & CMD_FLAG_REQUEST   );
	}
	
	/* Parameters are valid, create the new object */
	CHECK_MALLOC(  new = malloc(sizeof(_dict_object_t))  );
	
	/* Initialize the data of the new object */
	init_object(new, type);
	init_object_data(&new->data, data, type);
	new->parent = parent;
	
	/* We will change the dictionary => acquire the write lock */
	CHECK_POSIX_DO(  ret = pthread_rwlock_wrlock(&dict_lock),  goto error_free  );
	
	/* Now link the object -- this also checks that no object with same keys already exists */
	switch (type) {
		case DICT_VENDOR:
			/* A vendor object is linked in the g_dict_vendors.list[0], by their id */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &g_dict_vendors.list[0], &new->list[0], (int (*)(void*, void *))order_vendor_by_id, (void **)ref ),
					goto error_unlock  );
			break;
		
		case DICT_APPLICATION:
			/* An application object is linked in the g_dict_applciations.list[0], by their id */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &g_dict_applications.list[0], &new->list[0], (int (*)(void*, void *))order_appli_by_id, (void **)ref ),
					goto error_unlock  );
			break;
		
		case DICT_TYPE:
			/* A type object is linked in g_list_types by its name */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &g_list_types, &new->list[0], (int (*)(void*, void *))order_type_by_name, (void **)ref ),
					goto error_unlock  );
			break;
		
		case DICT_TYPE_ENUM:
			/* A type_enum object is linked in it's parent 'type' object lists 1 and 2 by its name and values */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &_O(parent)->list[1], &new->list[0], (int (*)(void*, void *))order_enum_by_name, (void **)ref ),
					goto error_unlock  );
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &_O(parent)->list[2], &new->list[1], (int (*)(void*, void *))order_enum_by_val, (void **)ref ),
					{ uti_list_unlink(&new->list[0]); goto error_unlock; }  );
			break;
		
		case DICT_AVP:
			/* An avp object is linked in lists 1 and 2 of its vendor, by code and name */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &vendor->list[1], &new->list[0], (int (*)(void*, void *))order_avp_by_code, (void **)ref ),
					goto error_unlock  );
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &vendor->list[2], &new->list[1], (int (*)(void*, void *))order_avp_by_name, (void **)ref ),
					{ uti_list_unlink(&new->list[0]); goto error_unlock; }  );
			break;
			
		case DICT_COMMAND:
			/* A command object is linked in g_list_cmd_name and g_list_cmd_code by its name and code */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &g_list_cmd_code, &new->list[1], (int (*)(void*, void *))order_cmd_by_codefl, (void **)ref ),
					goto error_unlock  );
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &g_list_cmd_name, &new->list[0], (int (*)(void*, void *))order_cmd_by_name, (void **)ref ),
					{ uti_list_unlink(&new->list[1]); goto error_unlock; }  );
			break;
		
		case DICT_RULE:
			/* A rule object is linked in list[2] of its parent command or AVP by the name of the AVP it refers */
			CHECK_FCT_DO(  ret = uti_list_insert_ordered ( &_O(parent)->list[2], &new->list[0], (int (*)(void*, void *))order_rule_by_avpn, (void **)ref ),
					goto error_unlock  );
			break;
			
		default:
			ASSERT(0);
	}
	
	/* A new object has been created, increment the global counter */
	_OBINFO(new).count++;
	
	/* Unlock the dictionary */
	CHECK_POSIX_DO(  ret = pthread_rwlock_unlock(&dict_lock),  goto error_free  );
	
	/* Save the pointer to the new object */
	if (ref)
		*ref = new;
	
	return 0;
	
error_unlock:
	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict_lock),  /* continue */  );
error_free:
	free(new);
	return ret;
}

/* Search an object in the dictionary */
int dict_search ( dict_object_type_t type, int criteria, void * what, dict_object_t **result )
{
	int ret = 0;
	
	TRACE_ENTRY("%d(%s) %d %p %p", type, dict_obj_info[CHECK_TYPE(type) ? type : 0].name, criteria, what, result);
	
	/* Check param */
	CHECK_PARAMS( CHECK_TYPE(type) );
	
	/* Lock the dictionary for reading */
	CHECK_POSIX(  pthread_rwlock_rdlock(&dict_lock)  );
	
	/* Now call the type-specific search function */
	ret = dict_obj_info[type].search_fct (criteria, what, (_dict_object_t **)result);
	
	/* Unlock */
	CHECK_POSIX(  pthread_rwlock_unlock(&dict_lock)  );
	
	return ret;
}


/*******************************************************************************************************/
/*******************************************************************************************************/
/*                                                                                                     */
/*                                  The other module functions                                         */
/*                                                                                                     */
/*******************************************************************************************************/
/*******************************************************************************************************/


/* Initialize the dictionary */
int dict_init ( void )
{
	TRACE_ENTRY("");
	
	/* Sanity checks */
	ASSERT( (sizeof(type_base_name) / sizeof(type_base_name[0])) == (AVP_TYPE_MAX + 1) );
	ASSERT( (sizeof(dict_obj_info)  / sizeof(dict_obj_info[0]))  == (DICT_TYPE_MAX + 1) );
	
	/* Initialize the lock for the dictionary */
	CHECK_POSIX(  pthread_rwlock_init(&dict_lock, NULL)  );
	
	/* Initialize the sentinel for vendors and AVP lists */
	init_object( &g_dict_vendors, DICT_VENDOR );
	g_dict_vendors.data.vendor.vendor_name = "(no vendor)";
	g_dict_vendors.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */
	
	
	/* Initialize the sentinel for applciations */
	init_object( &g_dict_applications, DICT_APPLICATION );
	g_dict_applications.data.application.application_name = "Diameter Common Messages";
	g_dict_applications.list[0].o = NULL; /* overwrite since since element is also sentinel for this list. */
			
	/* Initialize the sentinel for types */
	uti_list_init ( &g_list_types, NULL );
	
	/* Initialize the sentinels for commands */
	uti_list_init ( &g_list_cmd_name, NULL );
	uti_list_init ( &g_list_cmd_code, NULL );
	
	/* Initialize the error command object */
	init_object( &g_dict_cmd_error, DICT_COMMAND );
	g_dict_cmd_error.data.cmd.cmd_name="(generic error format)";
	g_dict_cmd_error.data.cmd.cmd_flag_mask=CMD_FLAG_ERROR | CMD_FLAG_REQUEST | CMD_FLAG_RETRANSMIT;
	g_dict_cmd_error.data.cmd.cmd_flag_val =CMD_FLAG_ERROR;
	dict_cmd_error = (dict_object_t *) &g_dict_cmd_error;
	
	/* Initialize the objects defined in the Diameter Base Protocol */
	CHECK_FCT(  dict_base_protocol()  );
		
	/* Done */
	return 0;
}

/* Destroy the dictionary */
int dict_fini ( void )
{
	int i;
	
	TRACE_ENTRY("");
	
	/* Acquire the write lock to make sure no other operation is ongoing */
	CHECK_POSIX(  pthread_rwlock_wrlock(&dict_lock)  );
	
	/* Empty all the lists, free the elements */
	destroy_list ( &g_dict_cmd_error.list[2] );
	destroy_list ( &g_list_cmd_code );
	destroy_list ( &g_list_cmd_name );
	destroy_list ( &g_list_types );
	for (i=0; i< NB_LISTS_PER_OBJ; i++) {
		destroy_list ( &g_dict_applications.list[i] );
		destroy_list ( &g_dict_vendors.list[i] );
	}
	
	/* Dictionary is empty, now destroy the lock */
	CHECK_POSIX(  pthread_rwlock_unlock(&dict_lock)  );
	CHECK_POSIX(  pthread_rwlock_destroy(&dict_lock)  );
	
	return 0;
}

/* Iterate a callback on the rules for an object */
int dict_iterate_rules ( dict_object_t *parent, void * data, int (*cb)(void *, dict_rule_data_t *) )
{
	int ret = 0;
	uti_list_t * li;
	
	TRACE_ENTRY("%p %p %p", parent, data, cb);
	
	/* Check parameters */
	CHECK_PARAMS(  verify_object(_O(parent))  );
	CHECK_PARAMS(  (_O(parent)->type == DICT_COMMAND) 
			|| ((_O(parent)->type == DICT_AVP) && (_O(parent)->data.avp.avp_basetype == AVP_TYPE_GROUPED)) );
	TRACE_DEBUG (FULL, "Iterating on rules of %s: '%s'.", 
			_OBINFO(parent).name, 
			_O(parent)->type == DICT_COMMAND ? 
				  _O(parent)->data.cmd.cmd_name
				: _O(parent)->data.avp.avp_name);
	
	/* Acquire the read lock  */
	CHECK_POSIX(  pthread_rwlock_rdlock(&dict_lock)  );
	
	/* go through the list and call the cb on each rule data */
	for (li = &(_O(parent)->list[2]); li->next != &(_O(parent)->list[2]); li = li->next) {
		ret = (*cb)(data, &(_O(li->next->o)->data.rule));
		if (ret != 0)
			break;
	}
		
	/* Release the lock */
	CHECK_POSIX(  pthread_rwlock_unlock(&dict_lock)  );
	
	return ret;
}

/* Create the list of vendors. Returns a 0-terminated array, that must be freed after use. Returns NULL on error. */
uint32_t * dict_get_vendorid_list()
{
	uint32_t * ret = NULL;
	int i = 0;
	uti_list_t * li;
	
	TRACE_ENTRY();
	
	/* Acquire the read lock */
	CHECK_POSIX_DO(  pthread_rwlock_rdlock(&dict_lock), return NULL  );
	
	/* Allocate an array to contain all the elements */
	CHECK_MALLOC_DO( ret = calloc( dict_obj_info[DICT_VENDOR].count + 1, sizeof(uint32_t) ), goto out );
	
	/* Copy the vendors IDs */
	for (li = g_dict_vendors.list[0].next; li != &(g_dict_vendors.list[0]); li = li->next) {
		ret[i] = _O(li->o)->data.vendor.vendor_id;
		i++;
		ASSERT( i <= dict_obj_info[DICT_VENDOR].count );
	}
out:	
	/* Release the lock */
	CHECK_POSIX_DO(  pthread_rwlock_unlock(&dict_lock), return NULL  );
	
	return ret;
}

/* Return the location of the cb list for an object, after checking its type */
int dict_disp_cb(dict_object_type_t type, dict_object_t *dict, uti_list_t ** cb_list)
{
	TRACE_ENTRY("%p %p", dict, cb_list);
	CHECK_PARAMS( verify_object(_O(dict)) );
	CHECK_PARAMS( _OBINFO(dict).type == type );
	CHECK_PARAMS( cb_list );
	*cb_list = &_O(dict)->disp_cbs;
	return 0;
}

"Welcome to our mercurial repository"