* A yang type regex cache added, this helps the performance by avoiding re-running the regcomp command on every iteration.

* An XML namespace cache added (see `xml2ns()`)
* Better performance of XML whitespace parsing/scanning.
This commit is contained in:
Olof hagsand 2019-04-19 16:01:39 +02:00
parent 728c97ab6d
commit 8c36083e16
8 changed files with 214 additions and 19 deletions

View file

@ -146,7 +146,7 @@ generic_validate(yang_stmt *yspec,
* and call application callback validations. * and call application callback validations.
* @param[in] h Clicon handle * @param[in] h Clicon handle
* @param[in] db The startup database. The wanted backend state * @param[in] db The startup database. The wanted backend state
* @param[out] xtr Transformed XML * @param[in] td Transaction
* @param[out] cbret CLIgen buffer w error stmt if retval = 0 * @param[out] cbret CLIgen buffer w error stmt if retval = 0
* @retval -1 Error - or validation failed (but cbret not set) * @retval -1 Error - or validation failed (but cbret not set)
* @retval 0 Validation failed (with cbret set) * @retval 0 Validation failed (with cbret set)
@ -183,6 +183,10 @@ startup_common(clicon_handle h,
clicon_debug(1, "Reading startup config from %s", db); clicon_debug(1, "Reading startup config from %s", db);
if (xmldb_get(h, db, "/", &xt, msd) < 0) if (xmldb_get(h, db, "/", &xt, msd) < 0)
goto done; goto done;
if (xml_child_nr(xt) == 0){ /* If empty skip */
td->td_target = xt;
goto ok;
}
if (msd){ if (msd){
if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0) if ((ret = clixon_module_upgrade(h, xt, msd, cbret)) < 0)
goto done; goto done;
@ -225,6 +229,7 @@ startup_common(clicon_handle h,
/* 7. Call plugin transaction complete callbacks */ /* 7. Call plugin transaction complete callbacks */
if (plugin_transaction_complete(h, td) < 0) if (plugin_transaction_complete(h, td) < 0)
goto done; goto done;
ok:
retval = 1; retval = 1;
done: done:
if (msd) if (msd)

View file

@ -114,6 +114,7 @@ db_merge(clicon_handle h,
/*! Clixon startup startup mode: Commit startup configuration into running state /*! Clixon startup startup mode: Commit startup configuration into running state
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @param[in] db tmp or startup
* @param[out] cbret If status is invalid contains error message * @param[out] cbret If status is invalid contains error message
* @retval -1 Error * @retval -1 Error
* @retval 0 Validation failed * @retval 0 Validation failed
@ -150,6 +151,10 @@ startup_mode_startup(clicon_handle h,
int retval = -1; int retval = -1;
int ret; int ret;
if (strcmp(db, "running")==0){
clicon_err(OE_FATAL, 0, "Invalid startup db: %s", db);
goto done;
}
/* Load plugins and call plugin_init() */ /* Load plugins and call plugin_init() */
if (backend_plugin_initiate(h) != 0) if (backend_plugin_initiate(h) != 0)
goto done; goto done;
@ -255,6 +260,8 @@ startup_extraxml(clicon_handle h,
goto done; goto done;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
if (xt==NULL /* || xml_child_nr(xt)==0 */ ) /* This gives SEGV in test_feature */
goto ok;
/* Write (potentially modified) xml tree xt back to tmp /* Write (potentially modified) xml tree xt back to tmp
*/ */
if ((ret = xmldb_put(h, "tmp", OP_REPLACE, xt, if ((ret = xmldb_put(h, "tmp", OP_REPLACE, xt,
@ -265,6 +272,7 @@ startup_extraxml(clicon_handle h,
goto fail; goto fail;
if (ret == 0) if (ret == 0)
goto fail; goto fail;
ok:
retval = 1; retval = 1;
done: done:
if (xt) if (xt)

View file

@ -218,7 +218,9 @@ struct yang_stmt{
Y_TYPE & identity: store all derived types Y_TYPE & identity: store all derived types
*/ */
yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */ yang_type_cache *ys_typecache; /* If ys_keyword==Y_TYPE, cache all typedef data except unions */
void *ys_regex_cache; /* regex cache */
int _ys_vector_i; /* internal use: yn_each */ int _ys_vector_i; /* internal use: yn_each */
}; };
typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg); typedef int (yang_applyfn_t)(yang_stmt *ys, void *arg);
@ -232,6 +234,8 @@ enum rfc_6020 yang_keyword_get(yang_stmt *ys);
char *yang_argument_get(yang_stmt *ys); char *yang_argument_get(yang_stmt *ys);
cg_var *yang_cv_get(yang_stmt *ys); cg_var *yang_cv_get(yang_stmt *ys);
cvec *yang_cvec_get(yang_stmt *ys); cvec *yang_cvec_get(yang_stmt *ys);
void *yang_regex_cache_get(yang_stmt *ys);
int yang_regex_cache_set(yang_stmt *ys, void *regex);
/* Other functions */ /* Other functions */
yang_stmt *yspec_new(void); yang_stmt *yspec_new(void);

View file

@ -127,6 +127,7 @@ struct xml{
reference, dont free */ reference, dont free */
cg_var *x_cv; /* Cached value as cligen variable cg_var *x_cv; /* Cached value as cligen variable
(eg xml_cmp) */ (eg xml_cmp) */
char *x_ns_cache; /* Cached namespace */
int _x_vector_i; /* internal use: xml_child_each */ int _x_vector_i; /* internal use: xml_child_each */
int _x_i; /* internal use for sorting: int _x_i; /* internal use for sorting:
see xml_enumerate and xml_cmp */ see xml_enumerate and xml_cmp */
@ -234,6 +235,7 @@ xml_prefix_set(cxobj *xn,
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* @see xmlns_check XXX can these be merged? * @see xmlns_check XXX can these be merged?
* @note, this function uses a cache. Any case where cache should be cleared?
*/ */
int int
xml2ns(cxobj *x, xml2ns(cxobj *x,
@ -241,9 +243,11 @@ xml2ns(cxobj *x,
char **namespace) char **namespace)
{ {
int retval = -1; int retval = -1;
char *ns; char *ns = NULL;
cxobj *xp; cxobj *xp;
if ((ns = x->x_ns_cache) != NULL)
goto ok;
if (prefix != NULL) /* xmlns:<prefix>="<uri>" */ if (prefix != NULL) /* xmlns:<prefix>="<uri>" */
ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR); ns = xml_find_type_value(x, "xmlns", prefix, CX_ATTR);
else /* xmlns="<uri>" */ else /* xmlns="<uri>" */
@ -261,6 +265,11 @@ xml2ns(cxobj *x,
ns = DEFAULT_XML_RPC_NAMESPACE; ns = DEFAULT_XML_RPC_NAMESPACE;
#endif #endif
} }
if (ns && (x->x_ns_cache = strdup(ns)) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
ok:
if (namespace) if (namespace)
*namespace = ns; *namespace = ns;
retval = 0; retval = 0;
@ -1359,6 +1368,8 @@ xml_free(cxobj *x)
free(x->x_childvec); free(x->x_childvec);
if (x->x_cv) if (x->x_cv)
cv_free(x->x_cv); cv_free(x->x_cv);
if (x->x_ns_cache)
free(x->x_ns_cache);
free(x); free(x);
return 0; return 0;
} }

View file

@ -132,10 +132,10 @@ ncname {namestart}{namechar}*
<STATEA>"<?" { BEGIN(PIDECL); return BQMARK; } <STATEA>"<?" { BEGIN(PIDECL); return BQMARK; }
<STATEA>\< { BEGIN(START); return *clixon_xml_parsetext; } <STATEA>\< { BEGIN(START); return *clixon_xml_parsetext; }
<STATEA>& { _YA->ya_lex_state =STATEA;BEGIN(AMPERSAND);} <STATEA>& { _YA->ya_lex_state =STATEA;BEGIN(AMPERSAND);}
<STATEA>[ \t] { clixon_xml_parselval.string = yytext;return WHITESPACE; } <STATEA>[ \t]+ { clixon_xml_parselval.string = yytext;return WHITESPACE; }
<STATEA>\r\n { clixon_xml_parselval.string = "\n";return WHITESPACE; } <STATEA>\r\n { clixon_xml_parselval.string = "\n"; _YA->ya_linenum++; return WHITESPACE; }
<STATEA>\r { clixon_xml_parselval.string = "\n";return WHITESPACE; } <STATEA>\r { clixon_xml_parselval.string = "\n";return WHITESPACE; }
<STATEA>\n { clixon_xml_parselval.string = yytext; _YA->ya_linenum++;return WHITESPACE; } <STATEA>\n { clixon_xml_parselval.string = "\n"; _YA->ya_linenum++;return WHITESPACE; }
<STATEA>. { clixon_xml_parselval.string = yytext; return CHARDATA; } <STATEA>. { clixon_xml_parselval.string = yytext; return CHARDATA; }
/* @see xml_chardata_encode */ /* @see xml_chardata_encode */

View file

@ -112,6 +112,44 @@ xml_parse_content(struct xml_parse_yacc_arg *ya,
return retval; return retval;
} }
/*! Add whitespace
* If text, ie only body, keep as is.
* But if there is an element, then skip all whitespace.
*/
static int
xml_parse_whitespace(struct xml_parse_yacc_arg *ya,
char *str)
{
cxobj *xn = ya->ya_xelement;
cxobj *xp = ya->ya_xparent;
int retval = -1;
int i;
ya->ya_xelement = NULL; /* init */
/* If there is an element already, only add one whitespace child
* otherwise, keep all whitespace.
*/
#if 1
for (i=0; i<xml_child_nr(xp); i++){
if (xml_type(xml_child_i(xp, i)) == CX_ELMNT)
goto ok; /* Skip if already element */
}
#endif
if (xn == NULL){
if ((xn = xml_new("body", xp, NULL)) == NULL)
goto done;
xml_type_set(xn, CX_BODY);
}
if (xml_value_append(xn, str)==NULL)
goto done;
ya->ya_xelement = xn;
ok:
retval = 0;
done:
return retval;
}
static int static int
xml_parse_version(struct xml_parse_yacc_arg *ya, xml_parse_version(struct xml_parse_yacc_arg *ya,
char *ver) char *ver)
@ -243,11 +281,17 @@ xml_parse_bslash1(struct xml_parse_yacc_arg *ya,
while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL) while ((xc = xml_child_each(x, xc, CX_ELMNT)) != NULL)
break; break;
if (xc != NULL){ /* at least one element */ if (xc != NULL){ /* at least one element */
xc = NULL; int i;
while ((xc = xml_child_each(x, xc, CX_BODY)) != NULL) { for (i=0; i<xml_child_nr(x);){
xml_purge(xc); xc = xml_child_i(x, i);
xc = NULL; /* reset iterator */ if (xml_type(xc) != CX_BODY){
} i++;
continue;
}
if (xml_child_rm(x, i) < 0)
goto done;
xml_free(xc);
}
} }
} }
retval = 0; retval = 0;
@ -425,7 +469,7 @@ content : element { clicon_debug(2, "content -> element"); }
| pi { clicon_debug(2, "content -> pi"); } | pi { clicon_debug(2, "content -> pi"); }
| CHARDATA { if (xml_parse_content(_YA, $1) < 0) YYABORT; | CHARDATA { if (xml_parse_content(_YA, $1) < 0) YYABORT;
clicon_debug(2, "content -> CHARDATA %s", $1); } clicon_debug(2, "content -> CHARDATA %s", $1); }
| WHITESPACE { if (xml_parse_content(_YA, $1) < 0) YYABORT; | WHITESPACE { if (xml_parse_whitespace(_YA, $1) < 0) YYABORT;
clicon_debug(2, "content -> WHITESPACE %s", $1); } clicon_debug(2, "content -> WHITESPACE %s", $1); }
| { clicon_debug(2, "content -> "); } | { clicon_debug(2, "content -> "); }
; ;

View file

@ -69,6 +69,7 @@
#include <sys/param.h> #include <sys/param.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <libgen.h> #include <libgen.h>
#include <regex.h>
/* cligen */ /* cligen */
#include <cligen/cligen.h> #include <cligen/cligen.h>
@ -222,6 +223,20 @@ yang_cvec_get(yang_stmt *ys)
return ys->ys_cvec; return ys->ys_cvec;
} }
void*
yang_regex_cache_get(yang_stmt *ys)
{
return ys->ys_regex_cache;
}
int
yang_regex_cache_set(yang_stmt *ys,
void *regex)
{
ys->ys_regex_cache = regex;
return 0;
}
/* End access functions */ /* End access functions */
/*! Create new yang specification /*! Create new yang specification
@ -266,6 +281,17 @@ ys_new(enum rfc_6020 keyw)
return ys; return ys;
} }
static int
yang_regex_cache_free(yang_stmt *ys)
{
if (ys->ys_regex_cache){
regfree(ys->ys_regex_cache);
free(ys->ys_regex_cache);
}
return 0;
}
/*! Free a single yang statement */ /*! Free a single yang statement */
static int static int
ys_free1(yang_stmt *ys) ys_free1(yang_stmt *ys)
@ -280,6 +306,7 @@ ys_free1(yang_stmt *ys)
cvec_free(ys->ys_cvec); cvec_free(ys->ys_cvec);
if (ys->ys_typecache) if (ys->ys_typecache)
yang_type_cache_free(ys->ys_typecache); yang_type_cache_free(ys->ys_typecache);
yang_regex_cache_free(ys);
free(ys); free(ys);
return 0; return 0;
} }

View file

@ -49,6 +49,7 @@
#include <regex.h> #include <regex.h>
#include <syslog.h> #include <syslog.h>
#include <assert.h> #include <assert.h>
#include <regex.h>
#include <netinet/in.h> #include <netinet/in.h>
/* cligen */ /* cligen */
@ -99,6 +100,74 @@ static const map_str2int ytmap[] = {
{NULL, -1} {NULL, -1}
}; };
/*! Regular expression compiling
* @retval -1 Error
* @retval 0 regex problem (no match?)
* @retval 1 OK Match
* @see match_regexp the CLIgen original composite function
*/
static int
regex_compile(char *pattern0,
regex_t *re)
{
int retval = -1;
char pattern[1024];
// char errbuf[1024];
int len0;
int status;
len0 = strlen(pattern0);
if (len0 > sizeof(pattern)-5){
clicon_err(OE_XML, EINVAL, "pattern too long");
goto done;
}
strncpy(pattern, "^(", 2);
strncpy(pattern+2, pattern0, sizeof(pattern)-2);
strncat(pattern, ")$", sizeof(pattern)-len0-1);
if ((status = regcomp(re, pattern, REG_NOSUB|REG_EXTENDED)) != 0) {
#if 0 /* ignore error msg for now */
regerror(status, re, errbuf, sizeof(errbuf));
#endif
goto fail;
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/*! Regular expression execution
* @retval -1 Error
* @retval 0 regex problem (no match?)
* @retval 1 OK Match
* @see match_regexp the CLIgen original composite function
*/
static int
regex_exec(regex_t *re,
char *string)
{
int retval = -1;
int status;
// char errbuf[1024];
status = regexec(re, string, (size_t) 0, NULL, 0);
if (status != 0) {
#if 0 /* ignore error msg for now */
regerror(status, re, errbuf, sizeof(errbuf));
#endif
goto fail;
}
retval = 1;
done:
return retval;
fail:
retval = 0;
goto done;
}
/* return 1 if built-in, 0 if not */ /* return 1 if built-in, 0 if not */
static int static int
yang_builtin(char *type) yang_builtin(char *type)
@ -525,14 +594,41 @@ cv_validate1(cg_var *cv,
} }
if ((options & YANG_OPTIONS_PATTERN) != 0){ if ((options & YANG_OPTIONS_PATTERN) != 0){
char *posix = NULL; char *posix = NULL;
if (regexp_xsd2posix(pattern, &posix) < 0) regex_t *re = NULL;
goto done;
if ((retval2 = match_regexp(str?str:"", posix)) < 0){ if ((re = yang_regex_cache_get(yrestype)) == NULL){
clicon_err(OE_DB, 0, "match_regexp: %s", pattern); /* Transform to posix regex */
return -1; if (regexp_xsd2posix(pattern, &posix) < 0)
goto done;
/* Create regex cache */
if ((re = malloc(sizeof(*re))) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(re, 0, sizeof(*re));
/* Compute regex pattern for use in patterns */
if ((retval2 = regex_compile(posix, re)) < 0)
goto done;
if (retval2 == 0){
if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
str, pattern);
goto fail;
break;
}
yang_regex_cache_set(yrestype, re);
if (posix)
free(posix);
}
if ((retval2 = regex_exec(re, str?str:"")) < 0)
goto done;
if (retval2 == 0){
if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
str, pattern);
goto fail;
break;
} }
if (posix)
free(posix);
if (retval2 == 0){ if (retval2 == 0){
if (reason) if (reason)
*reason = cligen_reason("regexp match fail: \"%s\" does not match %s", *reason = cligen_reason("regexp match fail: \"%s\" does not match %s",
@ -667,7 +763,7 @@ ys_cv_validate_union(yang_stmt *ys,
/*! Validate cligen variable cv using yang statement as spec /*! Validate cligen variable cv using yang statement as spec
* *
* @param[in] cv A cligen variable to validate. This is a correctly parsed cv. * @param[in] cv A cligen variable to validate. This is a correctly parsed cv.
* @param[in] ys A yang statement, must be leaf of leaf-list. * @param[in] ys A yang statement, must be leaf or leaf-list.
* @param[out] reason If given, and if return value is 0, contains a malloced string * @param[out] reason If given, and if return value is 0, contains a malloced string
* describing the reason why the validation failed. Must be freed. * describing the reason why the validation failed. Must be freed.
* @retval -1 Error (fatal), with errno set to indicate error * @retval -1 Error (fatal), with errno set to indicate error