====== eXtended Attribute Value Pairs - XAVP ======
Proposal and prototype implementation by:
Daniel-Constantin Mierla
CODE IN GIT REPOSITORY BRANCH: daniel/xavp
===== 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
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: <> (e.g., <>), good for debug purposes.
This type of value is intended for internal usage, one of the modules that needs it immediately being **dialog**.
=== XAVP data value ===
This allow to store within an xavp another xavp or list of xavps.
Example of usage (c-like pseudo-code):
#include
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.
* **reduce number of parameters for some modules**
* a module can reserve now only one name for xavp in default list. Inner xavp can have predefined names, without the risk for overlapping with other modules or config
* **dispatcher** module can reserve xavp name "dispatcher" via modparam, while the names for AVPs dst, cnt, grp can be predefined and documented in readme.
===== 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
* **dialog** module - store using generic data structure the dailog context pointer (get rid of current hack that adds a fake callback structure)
* serial forking functions
* possible in **lcr** or **carrierroute**
* possible in **utils**, to store each line of http response in a grouped xavp list.
===== Observations =====
* it is not a replacement to existing AVPs, but an addition, a new class of pseudo-variables