lyb UPDATE support for nested ext data
Needed LYB format change.
diff --git a/src/lyb.h b/src/lyb.h
index 165dfe5..67f76c7 100644
--- a/src/lyb.h
+++ b/src/lyb.h
@@ -51,7 +51,7 @@
sb = siblings_start
se = siblings_end
siblings = zero-LYB_SIZE_BYTES | (sb instance+ se)
- instance = model hash node
+ instance = node_type model hash node
model = 16bit_zero | (model_name_length model_name revision)
node = opaq | leaflist | list | any | inner | leaf
opaq = opaq_data siblings
@@ -66,6 +66,16 @@
*/
/**
+ * @brief LYB data node type
+ */
+enum lylyb_node_type {
+ LYB_NODE_TOP, /**< top-level node */
+ LYB_NODE_CHILD, /**< child node with a parent */
+ LYB_NODE_OPAQ, /**< opaque node */
+ LYB_NODE_EXT /**< nested extension data node */
+};
+
+/**
* @brief LYB format parser context
*/
struct lylyb_ctx {
@@ -101,7 +111,7 @@
#define LYB_SIBLING_STEP 4
/* current LYB format version */
-#define LYB_VERSION_NUM 0x03
+#define LYB_VERSION_NUM 0x04
/* LYB format version mask of the header byte */
#define LYB_VERSION_MASK 0x0F
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 43eafc1..25295ef 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -30,6 +30,7 @@
#include "log.h"
#include "parser_data.h"
#include "parser_internal.h"
+#include "plugins_exts.h"
#include "set.h"
#include "tree.h"
#include "tree_data.h"
@@ -331,67 +332,86 @@
}
/**
- * @brief Parse YANG model info.
+ * @brief Read YANG model info.
*
* @param[in] lybctx LYB context.
- * @param[in] parse_options Flag with options for parsing.
- * @param[out] model Parsed module.
+ * @param[out] mod_name Module name, if any.
+ * @param[out] mod_rev Module revision, "" if none.
* @return LY_ERR value.
*/
static LY_ERR
-lyb_parse_model(struct lylyb_ctx *lybctx, uint32_t parse_options, const struct lys_module **model)
+lyb_read_model(struct lylyb_ctx *lybctx, char **mod_name, char mod_rev[])
{
- LY_ERR ret = LY_SUCCESS;
- const struct lys_module *mod = NULL;
- char *mod_name = NULL, mod_rev[LY_REV_SIZE];
uint16_t rev, length;
- lyb_read_number(&length, 2, 2, lybctx);
+ *mod_name = NULL;
+ mod_rev[0] = '\0';
- if (length) {
- mod_name = malloc((length + 1) * sizeof *mod_name);
- LY_CHECK_ERR_RET(!mod_name, LOGMEM(lybctx->ctx), LY_EMEM);
- lyb_read(((uint8_t *)mod_name), length, lybctx);
- mod_name[length] = '\0';
- } else {
- goto cleanup;
+ lyb_read_number(&length, 2, 2, lybctx);
+ if (!length) {
+ return LY_SUCCESS;
}
- /* revision */
+ /* module name */
+ *mod_name = malloc((length + 1) * sizeof *mod_name);
+ LY_CHECK_ERR_RET(!*mod_name, LOGMEM(lybctx->ctx), LY_EMEM);
+ lyb_read(((uint8_t *)*mod_name), length, lybctx);
+ (*mod_name)[length] = '\0';
+
+ /* module revision */
lyb_read_number(&rev, sizeof rev, 2, lybctx);
if (rev) {
sprintf(mod_rev, "%04u-%02u-%02u", ((rev & LYB_REV_YEAR_MASK) >> LYB_REV_YEAR_SHIFT) + LYB_REV_YEAR_OFFSET,
(rev & LYB_REV_MONTH_MASK) >> LYB_REV_MONTH_SHIFT, rev & LYB_REV_DAY_MASK);
- mod = ly_ctx_get_module(lybctx->ctx, mod_name, mod_rev);
- if ((parse_options & LYD_PARSE_LYB_MOD_UPDATE) && !mod) {
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse YANG model info.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parse_options Flag with options for parsing.
+ * @param[out] mod Parsed module.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_model(struct lylyb_ctx *lybctx, uint32_t parse_options, const struct lys_module **mod)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const struct lys_module *m = NULL;
+ char *mod_name = NULL, mod_rev[LY_REV_SIZE];
+
+ /* read module info */
+ if ((ret = lyb_read_model(lybctx, &mod_name, mod_rev))) {
+ goto cleanup;
+ }
+
+ /* get the module */
+ if (mod_rev[0]) {
+ m = ly_ctx_get_module(lybctx->ctx, mod_name, mod_rev);
+ if ((parse_options & LYD_PARSE_LYB_MOD_UPDATE) && !m) {
/* try to use an updated module */
- mod = ly_ctx_get_module_implemented(lybctx->ctx, mod_name);
- if (mod && (!mod->revision || (strcmp(mod->revision, mod_rev) < 0))) {
+ m = ly_ctx_get_module_implemented(lybctx->ctx, mod_name);
+ if (m && (!m->revision || (strcmp(m->revision, mod_rev) < 0))) {
/* not an implemented module in a newer revision */
- mod = NULL;
+ m = NULL;
}
}
} else {
- mod = ly_ctx_get_module_latest(lybctx->ctx, mod_name);
+ m = ly_ctx_get_module_latest(lybctx->ctx, mod_name);
}
- /* TODO data_clb supported?
- if (lybctx->ctx->data_clb) {
- if (!*mod) {
- *mod = lybctx->ctx->data_clb(lybctx->ctx, mod_name, NULL, 0, lybctx->ctx->data_clb_data);
- } else if (!(*mod)->implemented) {
- *mod = lybctx->ctx->data_clb(lybctx->ctx, mod_name, (*mod)->ns, LY_MODCLB_NOT_IMPLEMENTED, lybctx->ctx->data_clb_data);
- }
- }*/
- if (!mod || !mod->implemented) {
+ if (!m || !m->implemented) {
if (parse_options & LYD_PARSE_STRICT) {
- if (!mod) {
+ if (!m) {
LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, missing module \"%s%s%s\".",
- mod_name, rev ? "@" : "", rev ? mod_rev : "");
- } else if (!mod->implemented) {
+ mod_name, mod_rev[0] ? "@" : "", mod_rev[0] ? mod_rev : "");
+ } else if (!m->implemented) {
LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, module \"%s%s%s\" not implemented.",
- mod_name, rev ? "@" : "", rev ? mod_rev : "");
+ mod_name, mod_rev[0] ? "@" : "", mod_rev[0] ? mod_rev : "");
}
ret = LY_EINVAL;
goto cleanup;
@@ -399,13 +419,13 @@
}
- if (mod) {
+ if (m) {
/* fill cached hashes, if not already */
- lyb_cache_module_hash(mod);
+ lyb_cache_module_hash(m);
}
cleanup:
- *model = mod;
+ *mod = m;
free(mod_name);
return ret;
}
@@ -748,7 +768,7 @@
break;
}
/* skip schema nodes from models not present during printing */
- if (lyb_has_schema_model(sibling, lybctx->lybctx->models) &&
+ if (((sibling->module->ctx != lybctx->lybctx->ctx) || lyb_has_schema_model(sibling, lybctx->lybctx->models)) &&
lyb_is_schema_hash_match((struct lysc_node *)sibling, hash, hash_count)) {
/* match found */
break;
@@ -776,6 +796,50 @@
}
/**
+ * @brief Parse schema node name of a nested extension data node.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent.
+ * @param[in] mod_name Module name of the node.
+ * @param[out] snode Parsed found schema node of a nested extension.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_schema_nested_ext(struct lyd_lyb_ctx *lybctx, const struct lyd_node *parent, const char *mod_name,
+ const struct lysc_node **snode)
+{
+ LY_ERR rc = LY_SUCCESS, r;
+ char *name = NULL;
+ struct lysc_ext_instance *ext;
+
+ assert(parent);
+
+ /* read schema node name */
+ LY_CHECK_GOTO(rc = lyb_read_string(&name, sizeof(uint16_t), lybctx->lybctx), cleanup);
+
+ /* check for extension data */
+ r = ly_nested_ext_schema(parent, NULL, mod_name, mod_name ? strlen(mod_name) : 0, LY_VALUE_JSON, NULL, name,
+ strlen(name), snode, &ext);
+ if (r == LY_ENOT) {
+ /* failed to parse */
+ LOGERR(lybctx->lybctx->ctx, LY_EINVAL, "Failed to parse node \"%s\" as nested extension instance data.", name);
+ rc = LY_EINVAL;
+ goto cleanup;
+ } else if (r) {
+ /* error */
+ rc = r;
+ goto cleanup;
+ }
+
+ /* fill cached hashes in the module, it may be from a different context */
+ lyb_cache_module_hash((*snode)->module);
+
+cleanup:
+ free(name);
+ return rc;
+}
+
+/**
* @brief Read until the end of the current siblings.
*
* @param[in] lybctx LYB context.
@@ -849,7 +913,11 @@
struct ly_set *parsed)
{
/* insert, keep first pointer correct */
- lyd_insert_node(parent, first_p, node, lybctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+ if (parent && (LYD_CTX(parent) != LYD_CTX(node))) {
+ lyd_insert_ext(parent, node);
+ } else {
+ lyd_insert_node(parent, first_p, node, lybctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0);
+ }
while (!parent && (*first_p)->prev->next) {
*first_p = (*first_p)->prev;
}
@@ -1363,12 +1431,12 @@
}
/**
- * @brief Parse node.
+ * @brief Parse a node.
*
- * @param[in] out Out structure.
- * @param[in,out] printed_node Current data node to print. Sets to the last printed node.
- * @param[in,out] sibling_ht Cached hash table for these siblings, created if NULL.
* @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the sibling, must be set if @p first_p is not.
+ * @param[in,out] first_p First top-level sibling, must be set if @p parent is not.
+ * @param[in,out] parsed Set of all successfully parsed nodes to add to.
* @return LY_ERR value.
*/
static LY_ERR
@@ -1378,19 +1446,33 @@
LY_ERR ret;
const struct lysc_node *snode;
const struct lys_module *mod;
+ enum lylyb_node_type lyb_type;
+ char *mod_name = NULL, mod_rev[LY_REV_SIZE];
- if (!parent || !parent->schema) {
- /* top-level or opaque, read module name */
- ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_opts, &mod);
- LY_CHECK_RET(ret);
+ /* read node type */
+ lyb_read_number(&lyb_type, sizeof lyb_type, 1, lybctx->lybctx);
+
+ switch (lyb_type) {
+ case LYB_NODE_TOP:
+ /* top-level, read module name */
+ LY_CHECK_GOTO(ret = lyb_parse_model(lybctx->lybctx, lybctx->parse_opts, &mod), cleanup);
/* read hash, find the schema node starting from mod */
- ret = lyb_parse_schema_hash(lybctx, NULL, mod, &snode);
- } else {
- /* read hash, find the schema node starting from parent schema */
- ret = lyb_parse_schema_hash(lybctx, parent->schema, NULL, &snode);
+ LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, NULL, mod, &snode), cleanup);
+ break;
+ case LYB_NODE_CHILD:
+ case LYB_NODE_OPAQ:
+ /* read hash, find the schema node starting from parent schema, if any */
+ LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, parent ? parent->schema : NULL, NULL, &snode), cleanup);
+ break;
+ case LYB_NODE_EXT:
+ /* ext, read module name */
+ LY_CHECK_GOTO(ret = lyb_read_model(lybctx->lybctx, &mod_name, mod_rev), cleanup);
+
+ /* read schema node name, find the nexted ext schema node */
+ LY_CHECK_GOTO(ret = lyb_parse_schema_nested_ext(lybctx, parent, mod_name, &snode), cleanup);
+ break;
}
- LY_CHECK_RET(ret);
if (!snode) {
ret = lyb_parse_node_opaq(lybctx, parent, first_p, parsed);
@@ -1405,8 +1487,10 @@
} else {
ret = lyb_parse_node_leaf(lybctx, parent, snode, first_p, parsed);
}
- LY_CHECK_RET(ret);
+ LY_CHECK_GOTO(ret, cleanup);
+cleanup:
+ free(mod_name);
return ret;
}
@@ -1423,18 +1507,15 @@
lyb_parse_siblings(struct lyd_lyb_ctx *lybctx, struct lyd_node *parent, struct lyd_node **first_p,
struct ly_set *parsed)
{
- LY_ERR ret;
ly_bool top_level;
top_level = !LY_ARRAY_COUNT(lybctx->lybctx->siblings);
/* register a new siblings */
- ret = lyb_read_start_siblings(lybctx->lybctx);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(lyb_read_start_siblings(lybctx->lybctx));
while (LYB_LAST_SIBLING(lybctx->lybctx).written) {
- ret = lyb_parse_node(lybctx, parent, first_p, parsed);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(lyb_parse_node(lybctx, parent, first_p, parsed));
if (top_level && !(lybctx->int_opts & LYD_INTOPT_WITH_SIBLINGS)) {
break;
@@ -1442,10 +1523,9 @@
}
/* end the siblings */
- ret = lyb_read_stop_siblings(lybctx->lybctx);
- LY_CHECK_RET(ret);
+ LY_CHECK_RET(lyb_read_stop_siblings(lybctx->lybctx));
- return ret;
+ return LY_SUCCESS;
}
/**
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index b330e4c..9364826 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -397,7 +397,7 @@
*
* @param[in] str String to write.
* @param[in] str_len Length of @p str.
- * @param[in] len_size Size of @ str_len in bytes.
+ * @param[in] len_size Size of @p str_len in bytes.
* @param[in] out Out structure.
* @param[in] lybctx LYB context.
* @return LY_ERR value.
@@ -456,20 +456,16 @@
lyb_print_model(struct ly_out *out, const struct lys_module *mod, struct lylyb_ctx *lybctx)
{
uint16_t revision;
+ int r;
/* model name length and model name */
- if (mod) {
- LY_CHECK_RET(lyb_write_string(mod->name, 0, sizeof(uint16_t), out, lybctx));
- } else {
- LY_CHECK_RET(lyb_write_number(0, 2, out, lybctx));
- return LY_SUCCESS;
- }
+ LY_CHECK_RET(lyb_write_string(mod->name, 0, sizeof(uint16_t), out, lybctx));
/* model revision as XXXX XXXX XXXX XXXX (2B) (year is offset from 2000)
* YYYY YYYM MMMD DDDD */
revision = 0;
- if (mod && mod->revision) {
- int r = atoi(mod->revision);
+ if (mod->revision) {
+ r = atoi(mod->revision);
r -= LYB_REV_YEAR_OFFSET;
r <<= LYB_REV_YEAR_SHIFT;
@@ -486,10 +482,8 @@
}
LY_CHECK_RET(lyb_write_number(revision, sizeof revision, out, lybctx));
- if (mod) {
- /* fill cached hashes, if not already */
- lyb_cache_module_hash(mod);
- }
+ /* fill cached hashes, if not already */
+ lyb_cache_module_hash(mod);
return LY_SUCCESS;
}
@@ -923,6 +917,35 @@
}
/**
+ * @brief Print LYB node type.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Current data node to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_lyb_type(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+ enum lylyb_node_type lyb_type;
+
+ if (node->flags & LYD_EXT) {
+ assert(node->schema);
+ lyb_type = LYB_NODE_EXT;
+ } else if (!node->schema) {
+ lyb_type = LYB_NODE_OPAQ;
+ } else if (!lysc_data_parent(node->schema)) {
+ lyb_type = LYB_NODE_TOP;
+ } else {
+ lyb_type = LYB_NODE_CHILD;
+ }
+
+ LY_CHECK_RET(lyb_write_number(lyb_type, 1, out, lybctx->lybctx));
+
+ return LY_SUCCESS;
+}
+
+/**
* @brief Print inner node.
*
* @param[in] out Out structure.
@@ -1156,15 +1179,21 @@
{
const struct lyd_node *node = *printed_node;
- /* write model info first, for all opaque and top-level nodes */
- if (!node->schema && (!node->parent || !node->parent->schema)) {
- LY_CHECK_RET(lyb_print_model(out, NULL, lybctx->lybctx));
- } else if (node->schema && !lysc_data_parent(node->schema)) {
+ /* write node type */
+ LY_CHECK_RET(lyb_print_lyb_type(out, node, lybctx));
+
+ /* write model info first */
+ if (node->schema && ((node->flags & LYD_EXT) || !lysc_data_parent(node->schema))) {
LY_CHECK_RET(lyb_print_model(out, node->schema->module, lybctx->lybctx));
}
- /* write schema hash */
- LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx->lybctx));
+ if (node->flags & LYD_EXT) {
+ /* write schema node name */
+ LY_CHECK_RET(lyb_write_string(node->schema->name, 0, sizeof(uint16_t), out, lybctx->lybctx));
+ } else {
+ /* write schema hash */
+ LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx->lybctx));
+ }
if (!node->schema) {
LY_CHECK_RET(lyb_print_node_opaq(out, (struct lyd_node_opaq *)node, lybctx));
diff --git a/tests/utests/extensions/test_schema_mount.c b/tests/utests/extensions/test_schema_mount.c
index 8b62281..29117d5 100644
--- a/tests/utests/extensions/test_schema_mount.c
+++ b/tests/utests/extensions/test_schema_mount.c
@@ -462,6 +462,7 @@
test_parse_inline(void **state)
{
const char *xml, *json;
+ char *lyb;
struct lyd_node *data;
/* valid */
@@ -649,6 +650,12 @@
CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, 0));
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ free(lyb);
lyd_free_siblings(data);
}
@@ -656,6 +663,7 @@
test_parse_shared(void **state)
{
const char *xml, *json;
+ char *lyb;
struct lyd_node *data;
ly_ctx_set_ext_data_clb(UTEST_LYCTX, test_ext_data_clb,
@@ -1011,6 +1019,12 @@
"}\n";
CHECK_PARSE_LYD_PARAM(json, LYD_JSON, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
CHECK_LYD_STRING_PARAM(data, json, LYD_JSON, LYD_PRINT_WITHSIBLINGS);
+
+ assert_int_equal(LY_SUCCESS, lyd_print_mem(&lyb, data, LYD_LYB, LYD_PRINT_WITHSIBLINGS));
+ lyd_free_siblings(data);
+
+ CHECK_PARSE_LYD_PARAM(lyb, LYD_LYB, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_SUCCESS, data);
+ free(lyb);
lyd_free_siblings(data);
}