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

@ -587,271 +587,262 @@ cvec2xml_1(cvec *cvv,
return retval;
}
/*! Return 1 if value is a body of one of the named children of xt */
static int
xml_is_body(cxobj *xt,
char *name,
char *val)
{
cxobj *x;
char *bx;
x = NULL;
while ((x = xml_child_each(xt, x, CX_ELMNT)) != NULL) {
if (strcmp(name, xml_name(x)))
continue;
if ((bx = xml_body(x)) == NULL)
continue;
if (strcmp(xml_body(x), val) == 0)
return 1;
}
return 0;
}
/*! Recursive help function to compute differences between two xml trees
* @see dbdiff_vector.
*/
static int
xml_diff1(yang_stmt *ys,
cxobj *xt1,
cxobj *xt2,
cxobj ***first,
size_t *firstlen,
cxobj ***second,
size_t *secondlen,
cxobj ***changed1,
cxobj ***changed2,
size_t *changedlen)
/*! Given child tree x1c, find matching child in base tree x0
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[out] x0cp Matching base tree child (if any)
* @note XXX: room for optimization? on 1K calls we have 1M body calls and
500K xml_child_each/cvec_each calls.
The outer loop is large for large lists
The inner loop is small
Major time in xml_find_body()
Can one do a binary search in the x0 list?
*/
int
match_base_child(cxobj *x0,
cxobj *x1c,
cxobj **x0cp,
yang_stmt *yc)
{
int retval = -1;
cxobj *x1 = NULL;
cxobj *x2 = NULL;
yang_stmt *y;
yang_stmt *ykey;
char *name;
cg_var *cvi;
char *x1cname;
cxobj *x0c = NULL; /* x0 child */
cvec *cvk = NULL; /* vector of index keys */
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *keyname;
int equal;
char *body1;
char *body2;
char **b1vec = NULL;
int i;
#if (XML_CHILD_HASH==1)
cxobj **p;
cbuf *key = NULL; /* cligen buffer hash key */
size_t vlen;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
/* Check nodes present in xt1 and xt2 + nodes only in xt1
* Loop over xt1
*/
x1 = NULL;
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
name = xml_name(x1);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name, 0);
else
y = yang_find_datanode((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
*x0cp = NULL; /* return value */
if (xml_hash(x0) == NULL)
goto nohash;
if ((key = cbuf_new()) == NULL){
clicon_err(OE_XML, errno, "cbuf_new");
goto done1;
}
if (xml_hash_key(x1c, yc, key) < 0)
goto done;
x0c = NULL;
if (cbuf_len(key))
if ((p = hash_value(xml_hash(x0), cbuf_get(key), &vlen)) != NULL){
assert(vlen == sizeof(x0c));
x0c = *p;
}
// fprintf(stderr, "%s get %s = 0x%x\n", __FUNCTION__, cbuf_get(key), (unsigned int)x0c);
*x0cp = x0c;
retval = 0;
done1:
if (key)
cbuf_free(key);
return retval;
nohash:
#endif /* XML_CHILD_HASH */
*x0cp = NULL; /* return value */
x1cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_CONTAINER: /* Equal regardless */
case Y_LEAF: /* Equal regardless */
x0c = xml_find(x0, x1cname);
break;
case Y_LEAF_LIST: /* Match with name and value */
if ((b1 = xml_body(x1c)) == NULL)
goto ok;
x0c = xml_find_body_obj(x0, x1cname, b1);
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, yc->ys_argument);
goto done;
}
switch (y->ys_keyword){
case Y_LIST:
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);
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL)
i++;
if ((b1vec = calloc(i, sizeof(b1))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");
goto done;
}
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((b1 = xml_find_body(x1c, keyname)) == NULL){
clicon_err(OE_UNIX, errno, "key %s not found", keyname);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
/* Iterate over xt2 tree to (1) find a child that matches name
(2) that have keys that matches */
b1vec[i++] = b1;
}
/* Iterate over x0 tree to (1) find a child that matches name
(2) that have keys that matches */
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL){
equal = 0;
x2 = NULL;
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x2), name))
continue;
cvi = NULL;
equal = 0;
/* (2) Match keys between x1 and x2 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((body1 = xml_find_body(x1, keyname)) == NULL)
continue; /* may be error */
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body1, body2)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
if (equal){
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
}
else
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
if (xml_diff1(y, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen)< 0)
goto done;
break;
case Y_LEAF:
if ((x2 = xml_find(xt2, name)) == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
}
body1 = xml_body(x1);
body2 = xml_body(x2);
if (body1 == NULL || body2 == NULL) /* empty type */
break;
if (strcmp(xml_body(x1), xml_body(x2))){
if (cxvec_append(x1, changed1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2, changed2, changedlen) < 0)
goto done;
}
break;
case Y_LEAF_LIST:
if ((body1 = xml_body(x1)) == NULL)
if (strcmp(xml_name(x0c), x1cname))
continue;
if (!xml_is_body(xt2, name, body1)) /* where body is */
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
break;
default:
break;
}
} /* while xt1 */
/* Check nodes present only in xt2
* Loop over xt2
*/
x2 = NULL;
while ((x2 = xml_child_each(xt2, x2, CX_ELMNT)) != NULL){
name = xml_name(x2);
if (ys->ys_keyword == Y_SPEC)
y = yang_find_topnode((yang_spec*)ys, name, 0);
else
y = yang_find_datanode((yang_node*)ys, name);
if (y == NULL){
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
goto done;
}
switch (y->ys_keyword){
case Y_LIST:
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;
/* Iterate over xt1 tree to (1) find a child that matches name
(2) that have keys that matches */
equal = 0;
x1 = NULL;
while ((x1 = xml_child_each(xt1, x1, CX_ELMNT)) != NULL){
if (strcmp(xml_name(x1), name))
continue;
cvi = NULL;
/* Must be inner loop */
cvi = NULL; i = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
b1 = b1vec[i++];
equal = 0;
/* (2) Match keys between x2 and x1 */
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
if ((body2 = xml_find_body(x2, keyname)) == NULL)
continue; /* may be error */
if ((body1 = xml_find_body(x1, keyname)) == NULL)
continue; /* may be error */
if (strcmp(body2, body1)==0)
equal=1;
else{
equal=0; /* stop as soon as inequal key found */
break;
}
}
if (equal) /* found x1 and x2 equal, otherwise look
for other x2 */
break;
keyname = cv_string_get(cvi);
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break; /* stop as soon as inequal key found */
equal=1; /* reaches here for all keynames, x0c is found. */
}
if (cvk){
cvec_free(cvk);
cvk = NULL;
}
if (!equal)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_CONTAINER:
/* Equal regardless */
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF:
if ((x1 = xml_find(xt1, name)) == NULL)
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
case Y_LEAF_LIST:
body2 = xml_body(x2);
if (!xml_is_body(xt1, name, body2)) /* where body is */
if (cxvec_append(x2, second, secondlen) < 0)
goto done;
break;
default:
break;
}
} /* while xt1 */
if (equal) /* x0c and x1c equal, otherwise look for other */
break;
} /* while x0c */
break;
default:
break;
}
ok:
*x0cp = x0c;
retval = 0;
done:
if (b1vec)
free(b1vec);
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Find next yang node, either start from yang_spec or some yang-node
* @param[in] y Node spec or sny yang-node
* @param[in] name Name of childnode to find
* @retval ys yang statement
* @retval NULL Error: no node found
*/
static yang_stmt *
yang_next(yang_node *y,
char *name)
{
yang_stmt *ys;
if (y->yn_keyword == Y_SPEC)
ys = yang_find_topnode((yang_spec*)y, name, 0);
else
ys = yang_find_datanode(y, name);
if (ys == NULL)
clicon_err(OE_UNIX, errno, "No yang node found: %s", name);
return ys;
}
/*! Recursive help function to compute differences between two xml trees
* @param[in] x1 First XML tree
* @param[in] x2 Second XML tree
* @param[out] x1vec Pointervector to XML nodes existing in only first tree
* @param[out] x1veclen Length of first vector
* @param[out] x2vec Pointervector to XML nodes existing in only second tree
* @param[out] x2veclen Length of x2vec vector
* @param[out] changed_x1 Pointervector to XML nodes changed orig value
* @param[out] changed_x2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
*/
static int
xml_diff1(yang_stmt *ys,
cxobj *x1,
cxobj *x2,
cxobj ***x1vec,
size_t *x1veclen,
cxobj ***x2vec,
size_t *x2veclen,
cxobj ***changed_x1,
cxobj ***changed_x2,
size_t *changedlen)
{
int retval = -1;
cxobj *x1c = NULL; /* x1 child */
cxobj *x2c = NULL; /* x2 child */
yang_stmt *yc;
char *b1;
char *b2;
clicon_debug(2, "%s: %s", __FUNCTION__, ys->ys_argument?ys->ys_argument:"yspec");
/* Check nodes present in x1 and x2 + nodes only in x1
* Loop over x1
* XXX: room for improvement. Compare with match_base_child()
*/
x1c = NULL;
while ((x1c = xml_child_each(x1, x1c, CX_ELMNT)) != NULL){
if ((yc = yang_next((yang_node*)ys, xml_name(x1c))) == NULL)
goto done;
if (match_base_child(x2, x1c, &x2c, yc) < 0)
goto done;
if (x2c == NULL){
if (cxvec_append(x1c, x1vec, x1veclen) < 0)
goto done;
}
else{
if (yc->ys_keyword == Y_LEAF){
if ((b1 = xml_body(x1c)) == NULL) /* empty type */
break;
if ((b2 = xml_body(x2c)) == NULL) /* empty type */
break;
if (strcmp(b1, b2)){
if (cxvec_append(x1c, changed_x1, changedlen) < 0)
goto done;
(*changedlen)--; /* append two vectors */
if (cxvec_append(x2c, changed_x2, changedlen) < 0)
goto done;
}
}
if (xml_diff1(yc, x1c, x2c,
x1vec, x1veclen,
x2vec, x2veclen,
changed_x1, changed_x2, changedlen)< 0)
goto done;
}
} /* while x1 */
/* Check nodes present only in x2
* Loop over x2
*/
x2c = NULL;
while ((x2c = xml_child_each(x2, x2c, CX_ELMNT)) != NULL){
if ((yc = yang_next((yang_node*)ys, xml_name(x2c))) == NULL)
goto done;
if (match_base_child(x1, x2c, &x1c, yc) < 0)
goto done;
if (x1c == NULL)
if (cxvec_append(x2c, x2vec, x2veclen) < 0)
goto done;
} /* while x1 */
retval = 0;
done:
return retval;
}
/*! Compute differences between two xml trees
* @param[in] yspec Yang specification
* @param[in] xt1 First XML tree
* @param[in] xt2 Second XML tree
* @param[in] x1 First XML tree
* @param[in] x2 Second XML tree
* @param[out] first Pointervector to XML nodes existing in only first tree
* @param[out] firstlen Length of first vector
* @param[out] second Pointervector to XML nodes existing in only second tree
* @param[out] secondlen Length of second vector
* @param[out] changed1 Pointervector to XML nodes changed value
* @param[out] changed2 Pointervector to XML nodes changed value
* @param[out] changed1 Pointervector to XML nodes changed orig value
* @param[out] changed2 Pointervector to XML nodes changed wanted value
* @param[out] changedlen Length of changed vector
* All xml vectors should be freed after use.
* Bot xml trees should be freed with xml_free()
*/
int
xml_diff(yang_spec *yspec,
cxobj *xt1,
cxobj *xt2,
cxobj *x1,
cxobj *x2,
cxobj ***first,
size_t *firstlen,
cxobj ***second,
@ -865,19 +856,19 @@ xml_diff(yang_spec *yspec,
*firstlen = 0;
*secondlen = 0;
*changedlen = 0;
if (xt1 == NULL && xt2 == NULL)
if (x1 == NULL && x2 == NULL)
return 0;
if (xt2 == NULL){
if (cxvec_append(xt1, first, firstlen) < 0)
if (x2 == NULL){
if (cxvec_append(x1, first, firstlen) < 0)
goto done;
goto ok;
}
if (xt1 == NULL){
if (cxvec_append(xt1, second, secondlen) < 0)
if (x1 == NULL){
if (cxvec_append(x1, second, secondlen) < 0)
goto done;
goto ok;
}
if (xml_diff1((yang_stmt*)yspec, xt1, xt2,
if (xml_diff1((yang_stmt*)yspec, x1, x2,
first, firstlen,
second, secondlen,
changed1, changed2, changedlen) < 0)
@ -1833,82 +1824,6 @@ api_path2xml(char *api_path,
return retval;
}
/*! Given a modification tree, check existing matching child in the base tree
* param[in] x0 Base tree node
* param[in] x1c Modification tree child
* param[in] yc Yang spec of tree child
* param[out] x0cp Matching base tree child (if any)
*/
static int
match_base_child(cxobj *x0,
cxobj *x1c,
yang_stmt *yc,
cxobj **x0cp)
{
int retval = -1;
cxobj *x0c = NULL;
char *keyname;
cvec *cvk = NULL;
cg_var *cvi;
char *b0;
char *b1;
yang_stmt *ykey;
char *cname;
int ok;
char *x1bstr; /* body string */
cname = xml_name(x1c);
switch (yc->ys_keyword){
case Y_LEAF_LIST: /* Match with name and value */
x1bstr = xml_body(x1c);
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(cname, xml_name(x0c)) == 0 &&
strcmp(xml_body(x0c), x1bstr)==0)
break;
}
break;
case Y_LIST: /* Match with key values */
if ((ykey = yang_find((yang_node*)yc, Y_KEY, NULL)) == NULL){
clicon_err(OE_XML, errno, "%s: List statement \"%s\" has no key",
__FUNCTION__, yc->ys_argument);
goto done;
}
/* The value is a list of keys: <key>[ <key>]* */
if ((cvk = yang_arg2cvec(ykey, " ")) == NULL)
goto done;
x0c = NULL;
while ((x0c = xml_child_each(x0, x0c, CX_ELMNT)) != NULL) {
if (strcmp(xml_name(x0c), cname))
continue;
cvi = NULL;
ok = 0;
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
keyname = cv_string_get(cvi);
ok = 1; /* if we come here */
if ((b0 = xml_find_body(x0c, keyname)) == NULL)
break; /* error case */
if ((b1 = xml_find_body(x1c, keyname)) == NULL)
break; /* error case */
if (strcmp(b0, b1))
break;
ok = 2; /* and reaches here for all keynames, x0c is found. */
}
if (ok == 2)
break;
}
break;
default: /* Just match with name */
x0c = xml_find(x0, cname);
break;
}
*x0cp = x0c;
retval = 0;
done:
if (cvk)
cvec_free(cvk);
return retval;
}
/*! Merge a base tree x0 with x1 with yang spec y
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
@ -1975,7 +1890,7 @@ xml_merge1(cxobj *x0,
}
/* See if there is a corresponding node in the base tree */
x0c = NULL;
if (yc && match_base_child(x0, x1c, yc, &x0c) < 0)
if (yc && match_base_child(x0, x1c, &x0c, yc) < 0)
goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
goto done;
@ -2012,7 +1927,7 @@ xml_merge(cxobj *x0,
goto done;
}
/* See if there is a corresponding node in the base tree */
if (match_base_child(x0, x1c, yc, &x0c) < 0)
if (match_base_child(x0, x1c, &x0c, yc) < 0)
goto done;
if (xml_merge1(x0c, (yang_node*)yc, x0, x1c) < 0)
goto done;