experimental xml hash for better performance

This commit is contained in:
Olof hagsand 2017-09-18 20:53:49 +02:00
parent 687641e944
commit 7a7bfc48a4
18 changed files with 612 additions and 481 deletions

View file

@ -34,6 +34,10 @@
* XML support functions.
*/
#ifdef HAVE_CONFIG_H
#include "clixon_config.h" /* generated by config & autoconf */
#endif
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@ -51,7 +55,11 @@
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_parse.h"
@ -81,6 +89,9 @@ struct xml{
void *x_spec; /* Pointer to specification, eg yang, by
reference, dont free */
cg_var *x_cv; /* If body this contains the typed value */
#if (XML_CHILD_HASH==1)
clicon_hash_t *x_hash; /* Hash of children */
#endif
};
/* Mapping between xml type <--> string */
@ -647,6 +658,9 @@ xml_purge(cxobj *xc)
int i;
cxobj *xp;
#if (XML_CHILD_HASH==1)
xml_hash_op(xc, 0);
#endif
if ((xp = xml_parent(xc)) != NULL){
/* Find child order i in parent*/
for (i=0; i<xml_child_nr(xp); i++)
@ -771,6 +785,9 @@ xml_rootchild(cxobj *xp,
* @retval NULL if no such node or no body in found node
* Note, make a copy of the return value to use it properly
* @see xml_find_body
* Explaining picture:
* xt --> xb (x_type=CX_BODY)
* return xb.x_value
*/
char *
xml_body(cxobj *xn)
@ -782,12 +799,18 @@ xml_body(cxobj *xn)
return NULL;
}
/*! Get (first) body of xml node, note could be many
* @param[in] xt xml tree node
* Explaining picture:
* xt --> xb (x_type=CX_BODY)
* return xb
*/
cxobj *
xml_body_get(cxobj *xn)
xml_body_get(cxobj *xt)
{
cxobj *xb = NULL;
while ((xb = xml_child_each(xn, xb, CX_BODY)) != NULL)
while ((xb = xml_child_each(xt, xb, CX_BODY)) != NULL)
return xb;
return NULL;
}
@ -795,22 +818,27 @@ xml_body_get(cxobj *xn)
/*! Find and return the value of a sub xml node
*
* The value can be of an attribute or body.
* @param[in] xn xml tree node
* @param[in] xt xml tree node
* @param[in] name name of xml tree nod (eg attr name or "body")
* @retval The returned value as a pointer to the name string
* @retval NULL if no such node or no value in found node
* @retval val Pointer to the name string
* @retval NULL No such node or no value in node
*
* Note, make a copy of the return value to use it properly
* See also xml_find_body
* Explaining picture:
* xt --> x
* x_name=name
* return x_value
*/
char *
xml_find_value(cxobj *x_up,
xml_find_value(cxobj *xt,
char *name)
{
cxobj *x;
cxobj *x = NULL;
if ((x = xml_find(x_up, name)) != NULL)
return xml_value(x);
while ((x = xml_child_each(xt, x, -1)) != NULL)
if (strcmp(name, xml_name(x)) == 0)
return xml_value(x);
return NULL;
}
@ -821,18 +849,56 @@ xml_find_value(cxobj *x_up,
* @retval NULL if no such node or no body in found node
* @note, make a copy of the return value to use it properly
* @see xml_find_value
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name return x_value
*/
char *
xml_find_body(cxobj *xn,
xml_find_body(cxobj *xt,
char *name)
{
cxobj *x;
cxobj *x=NULL;
if ((x = xml_find(xn, name)) != NULL)
return xml_body(x);
while ((x = xml_child_each(xt, x, -1)) != NULL)
if (strcmp(name, xml_name(x)) == 0)
return xml_body(x);
return NULL;
}
/*! Find xml object with matching name and value.
*
* This can be useful if x is a leaf-list with many subs with same name,
* but you need to pick the object with a specific value
* @param[in] xt XML tree
* @param[in] name Name of child (there can be many with same name)
* @param[in] val Value. Must be equal to body of child.
* @retval x Child with matching name and body
*
* Explaining picture:
* xt --> x --> bx (x_type=CX_BODY)
* x_name=name x_value=val
* return x
*/
cxobj *
xml_find_body_obj(cxobj *xt,
char *name,
char *val)
{
cxobj *x = NULL;
char *bstr;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(x)))
continue;
if ((bstr = xml_body(x)) == NULL)
continue;
if (strcmp(bstr, val) == 0)
break; /* x is returned */
}
return x;
}
/*! Free an xl sub-tree recursively, but do not remove it from parent
* @param[in] x the xml tree to be freed.
* @see xml_purge where x is also removed from parent
@ -856,6 +922,10 @@ xml_free(cxobj *x)
xml_free(xc);
x->x_childvec[i] = NULL;
}
#if (XML_CHILD_HASH==1)
if (x->x_hash)
hash_free(x->x_hash);
#endif
if (x->x_childvec)
free(x->x_childvec);
free(x);
@ -995,7 +1065,7 @@ clicon_xml2cbuf(cbuf *cb,
*/
int
xml_parse(char *str,
cxobj *x_up)
cxobj *xt)
{
int retval = -1;
struct xml_parse_yacc_arg ya = {0,};
@ -1004,7 +1074,7 @@ xml_parse(char *str,
clicon_err(OE_XML, errno, "%s: strdup", __FUNCTION__);
return -1;
}
ya.ya_xparent = x_up;
ya.ya_xparent = xt;
ya.ya_skipspace = 1; /* remove all non-terminal bodies (strip pretty-print) */
if (clixon_xml_parsel_init(&ya) < 0)
goto done;
@ -1641,6 +1711,160 @@ xml_operation2str(enum operation_type op)
}
}
#if (XML_CHILD_HASH==1)
/*! Return yang hash
* Not necessarily set. Either has not been set yet (by xml_spec_set( or anyxml.
*/
clicon_hash_t *
xml_hash(cxobj *x)
{
return x->x_hash;
}
int
xml_hash_init(cxobj *x)
{
if ((x->x_hash = hash_init()) < 0)
return -1;
return 0;
}
int
xml_hash_rm(cxobj *x)
{
if (x && x->x_hash){
hash_free(x->x_hash);
x->x_hash = NULL;
}
return 0;
}
/* Compute hash key for xml entry
* @param[in] x
* @param[in] y
* @param[out] key
* key: yangtype+x1name
* LEAFLIST: b0
* LIST: b2vec+b0 -> x0c
*/
int
xml_hash_key(cxobj *x,
yang_stmt *y,
cbuf *key)
{
int retval = -1;
yang_stmt *ykey;
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *keyname;
char *b;
char *str;
switch (y->ys_keyword){
case Y_CONTAINER: str = "c"; break;
case Y_LEAF: str = "e"; break;
case Y_LEAF_LIST: str = "l"; break;
case Y_LIST: str = "i"; break;
default:
str = "xx"; break;
break;
}
cprintf(key, "%s%s", str, xml_name(x));
switch (y->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
if ((b = xml_body(x)) == NULL){
cbuf_reset(key);
goto ok;
}
cprintf(key, "%s", xml_body(x));
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)y, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, y->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL;
while ((cvi = cvec_each(cvk, cvi)) != NULL){
keyname = cv_string_get(cvi);
if ((b = xml_find_body(x, keyname)) == NULL){
cbuf_reset(key);
goto ok;
}
cprintf(key, "/%s", b);
}
break;
default:
break;
}
ok:
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! XML hash add. Create hash and add key/value to parent
*
* @param[in] arg -1: rm only hash 0: rm entry, 1: add
* Typically called for a whole tree.
*/
int
xml_hash_op(cxobj *x,
void *arg)
{
int retval = -1;
cxobj *xp;
clicon_hash_t *ph;
yang_stmt *y;
cbuf *key = NULL; /* cligen buffer hash key */
int op = (int)arg;
if (xml_hash(x)==NULL){
if (op==1)
xml_hash_init(x);
}
else if (op==-1|| op==0)
xml_hash_rm(x);
if (op==-1)
goto ok;
if ((xp = xml_parent(x)) == NULL)
goto ok;
if ((ph = xml_hash(xp))==NULL)
goto ok;
if ((y = xml_spec(x)) == NULL)
goto ok;
if ((key = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done;
}
if (xml_hash_key(x, y, key) < 0)
goto done;
if (cbuf_len(key)){
// fprintf(stderr, "%s add %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x);
if (op == 1){
if (hash_add(ph, cbuf_get(key), &x, sizeof(x)) == NULL)
goto done;
}
else
if (hash_del(ph, cbuf_get(key)) < 0)
goto done;
}
ok:
retval = 0;
done:
if (key)
cbuf_free(key);
return retval;
}
#endif
/*
* Turn this on to get a xml parse and pretty print test program
* Usage: xpath