This is an old revision of the document!
Table of Contents
eXtended Attribute Value Pairs - XAVP
Proposal and prototype implementation by: Daniel-Constantin Mierla <miconda [at] gmail (dot) com
Purpose
It is not a replacement for current AVP implementation, but a new feature (a new data structure available in code and config, similar to pseudo-variable like: sahred variable $shv(…), hash table variable $sht(…), private variable $var(…)). Developers and script writers can decide which one to use: AVP or XAVP.
Primary goals:
- ability to store more value types, not only integer and string
- simplify addressing by using only string name for attributes
- possibility to optimise the search by creating lists of xavp lists
- availability in config file for xavp with string or integer value (expression interpreter supports only these values) via PV module
- availability in stateful processing, during the lifetime of transaction
Internal design
- one master list stored in core
- internal integer id to optimise the search in list (hash value over the string name)
- name stored as str and null terminated (can use regexp over it)
- string values stored as str and null terminated
- other types of value that can be used internally (not in config)
- long
- long long
- time
- xavp (or list of xavp)
- generic data structure
Data types definition
struct _sr_xavp; typedef enum { SR_XTYPE_NULL=0, SR_XTYPE_INT, SR_XTYPE_STR, SR_XTYPE_TIME, SR_XTYPE_LONG, SR_XTYPE_LLONG, SR_XTYPE_XAVP, SR_XTYPE_DATA } sr_xtype_t; typedef void (*sr_xavp_sfree_f)(void *d); typedef void (*sr_data_free_f)(void *d, sr_xavp_sfree_f sfree); typedef struct _sr_data { void *p; sr_data_free_f pfree; } sr_data_t; typedef struct _sr_xval { sr_xtype_t type; union { int i; str s; /* replicated */ time_t t; long l; long long ll; struct _sr_xavp *xavp; /* must be given in shm */ sr_data_t *data; /* must be given in shm */ } v; } sr_xval_t; typedef struct _sr_xavp { unsigned int id; str name; sr_xval_t val; struct _sr_xavp *next; } sr_xavp_t;
Generic data value
It allows to store any type of data built in shared memory. A structure of sr_data_t has to be allocated in shared memory and filled with two fields:
- p - pointer to your data - the data must be also allocated in shared memory
- pfree - function to free the data - it takes two functions as parameter - void pointer to data and a free function. The free function is provided by the core API, because depending on usage case it might be shm_free() or shm_free_unsafe()
Example of usage (c-like pseudo-code):
#include <xavp.h> struct _test { int a; char *s; } *d; sr_data_t *xdata; sr_xval_t xval; str xname = {"my-xavp", 7}; void test_free(void *d, sr_xavp_sfree_f sfree) { sfree(d->s); sfree(d); } data = shm_malloc(sizeof(sr_data_t)); d = shm_malloc(sizeof(struct _test)); d->s = shm_malloc(5); d->a = 10; strcpy(d->s, "test"); xdata->p = (void*)d; xdata->pfree = test_free; xval.type = SR_XTYPE_DATA; xval.v.data = xdata; xavp_add(&xname, &xval, NULL);
NOTE: this avp printed to xlog will return a string: «data:value of the pointer to data» (e.g., «data:0x973937393»), good for debug purposes.
XAVP data value
This allow to store within an xavp another xavp or list of xavps.
Example of usage (c-like pseudo-code):
#include <xavp.h> sr_avp_t *list; sr_xval_t xval; str xname; // build the list of xavp list = NULL; xval.type = SR_XTYPE_INT; xval.v.i = 10; xname.s = "avp1"; xname.len = 4; xavp_add(&xname, &xval, &list); xval.type = SR_XTYPE_INT; xval.v.i = 20; xname.s = "avp2"; xname.len = 4; xavp_add(&xname, &xval, &list); // add to default list the xavp having the value the above list xval.type = SR_XTYPE_XAVP; xval.v.xavp = list; xname.s = "avp3"; xname.len = 4; xavp_add(&xname, &xval, NULL); // add to default list another xavp xval.type = SR_XTYPE_INT; xval.v.i = 40; xname.s = "avp4"; xname.len = 4; xavp_add(&xname, &xval, NULL);
The content of xavp default list is now:
(avp4, 40), (avp3, ((avp2, 20), (avp1, 10)))
These types of xavp can be used directly in configuration file, via PV module, as long as inner xavp has integer or string value. To address them, the format is:
$xavp(avp3=>avp1)=10; # add new xavp 'avp3' with value being xavp 'avp1' with value 10 $xavp(avp3[0]=>avp2)=20; # add to first xavp 'avp3' a new xavp 'avp2' with value 20
IMPORTANT: in assignments, if index is missing, then a new xavp is added. If index is provided, like above [0], then an inner xavp is added to the xavp at respective position.
When getting the value, no index or [0] point to same xavp - first one with respective name.
Benefits
- grouping of value based on purpose
- for example, avps for serial forking:
$xavp(sf=>uri)="sip:10.10.10.10"; $xavp(sf[0]=>fr_timer)=10; $xavp(sf[0]=>fr_inv_timer)=15; $xavp(sf[0]=>headers)="X-CustomerID: 1234\r\n"; $xavp(sf=>uri)="sip:10.10.10.11"; $xavp(sf[0]=>fr_timer)=20; $xavp(sf[0]=>fr_inv_timer)=35; $xavp(sf=>uri)="sip:10.10.10.12"; $xavp(sf[0]=>fr_timer)=10; $xavp(sf[0]=>fr_inv_timer)=15; $xavp(sf[0]=>headers)="X-CustomerID: pw45\r\n";
For second destination, there is no extra header to be added. With current avp model, a dummy value must be set, otherwise the value from third destination will be used.
- faster lookup
- with current avp, getting to last avp (as in above example) would require 11 steps in iteration. With xavp it takes 7 steps. When dealing with lot of avps, the improvement is more significant if the grouping is used.
- from inside the code, the improvement is more visible, as the reference to inner xavp list can be kept at first lookup.
- further improvements to speed and flexibility by storing generic data structures
- example: serial forking function can build now the info in a shared memory structure
- less exposure the name conflicts by grouping
- modules can group their internal AVPs under a xavp, so other xavps with same name in global list won't affect the module functionality. Say dispatcher groups its avps under "dispatcher". Inside that list it can have xavp with names: dst, grp, cnt. In the global list or other sublists can be other avps with name 'dst', without any interference.
Use cases
Couple of use cases I have in mind now:
- acc module for multi-leg accounting - grouping will improve speed and remove the wrong behaviour when a value is not set in one leg - null is set for the right leg, not for the last one.
- dispatcher module - grouping
- serial forking functions