lyb parser & printer CHANGE base implementation finished

All basic features should be implemented, practically
not tested at all.
diff --git a/src/common.c b/src/common.c
index e08dba4..0f03b43 100644
--- a/src/common.c
+++ b/src/common.c
@@ -1372,7 +1372,7 @@
 static int
 lyb_ht_build_equal_cb(void *UNUSED(val1_p), void *UNUSED(val2_p), int UNUSED(mod), void *UNUSED(cb_data))
 {
-    /* for this purpose, if hash matches, the value must also */
+    /* for this purpose, if hash matches, the value does also, we do not want 2 values to have the same hash */
     return 1;
 }
 
@@ -1389,12 +1389,18 @@
 }
 
 LYB_HASH
-lyb_hash(const struct lys_node *sibling, uint8_t collision_id)
+lyb_hash(struct lys_node *sibling, uint8_t collision_id)
 {
     struct lys_module *mod;
     uint32_t full_hash;
     LYB_HASH hash;
 
+#ifdef LY_ENABLED_CACHE
+    if ((collision_id < LYS_NODE_HASH_COUNT) && sibling->hash[collision_id]) {
+        return sibling->hash[collision_id];
+    }
+#endif
+
     mod = lys_node_module(sibling);
 
     full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
@@ -1414,11 +1420,33 @@
     /* add colision identificator */
     hash |= LYB_HASH_COLLISION_ID >> collision_id;
 
+    /* save this hash */
+#ifdef LY_ENABLED_CACHE
+    if (collision_id < LYS_NODE_HASH_COUNT) {
+        sibling->hash[collision_id] = hash;
+    }
+#endif
+
     return hash;
 }
 
+int
+lyb_has_schema_model(struct lys_node *sibling, const struct lys_module **models, int mod_count)
+{
+    int i;
+    const struct lys_module *mod = lys_node_module(sibling);
+
+    for (i = 0; i < mod_count; ++i) {
+        if (mod == models[i]) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
 struct hash_table *
-lyb_hash_siblings(struct lys_node *sibling)
+lyb_hash_siblings(struct lys_node *sibling, const struct lys_module **models, int mod_count)
 {
     LYB_HASH hash;
     struct hash_table *ht;
@@ -1432,10 +1460,15 @@
     ht = lyht_new(1, sizeof(struct lys_node *), lyb_ht_build_equal_cb, NULL, 1);
     LY_CHECK_ERR_RETURN(!ht, LOGMEM(sibling->module->ctx), NULL);
 
-    parent = lys_parent(sibling);
+    for (parent = lys_parent(sibling); parent && (parent->nodetype == LYS_USES); parent = lys_parent(parent));
     mod = lys_node_module(sibling);
     sibling = NULL;
     while ((sibling = (struct lys_node *)lys_getnext(sibling, parent, mod, 0))) {
+        if (models && !lyb_has_schema_model(sibling, models, mod_count)) {
+            /* ignore models not present during printing */
+            continue;
+        }
+
         for (i = 0; i < LYB_HASH_BITS; ++i) {
             hash = lyb_hash(sibling, i);
             if (!hash) {
@@ -1454,10 +1487,6 @@
             lyht_free(ht);
             return NULL;
         }
-
-#ifdef LY_ENABLED_CACHE
-        sibling->hash = hash;
-#endif
     }
 
     /* change val equal callback so that the HT is usable for finding value hashes */
@@ -1465,28 +1494,3 @@
 
     return ht;
 }
-
-LYB_HASH
-lyb_hash_find(struct hash_table *ht, const struct lys_node *node)
-{
-    LYB_HASH hash;
-    uint32_t i;
-
-    for (i = 0; i < LYB_HASH_BITS; ++i) {
-        hash = lyb_hash(node, i);
-        if (!hash) {
-            lyht_free(ht);
-            return 0;
-        }
-
-        if (!lyht_find(ht, &node, hash, NULL)) {
-            /* success, no collision */
-            break;
-        }
-    }
-    /* cannot happen, we already calculated the hash */
-    assert(i <= LYB_HASH_BITS);
-
-    return hash;
-}
-
diff --git a/src/parser.h b/src/parser.h
index 44596f7..d680378 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -58,8 +58,8 @@
  * @defgroup lybdata LYB data format support
  * @{
  */
-struct lyd_node *lyd_parse_lyb(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *rpc_act,
-                               const struct lyd_node *data_tree, const char *yang_data_name, int *parsed);
+struct lyd_node *lyd_parse_lyb(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *data_tree,
+                               const char *yang_data_name, int *parsed);
 
 /**@} lybdata */
 
diff --git a/src/parser_json.c b/src/parser_json.c
index f75e6b5..1f720f8 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -1485,7 +1485,7 @@
     ly_set_free(set);
 
     /* add/validate default values, unres */
-    if (lyd_defaults_add_unres(&result, options, ctx, data_tree, act_notif, unres)) {
+    if (lyd_defaults_add_unres(&result, options, ctx, data_tree, act_notif, unres, 1)) {
         goto error;
     }
 
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 0a85f56..b4cd9bc 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -54,7 +54,9 @@
 
         /* we are actually reading some data, not just finishing another chunk */
         if (to_read) {
-            memcpy(buf, data + ret, to_read);
+            if (buf) {
+                memcpy(buf, data + ret, to_read);
+            }
 
             for (i = 0; i < lybs->used; ++i) {
                 /* decrease all written counters */
@@ -62,7 +64,9 @@
             }
             /* decrease count/buf */
             count -= to_read;
-            buf += to_read;
+            if (buf) {
+                buf += to_read;
+            }
 
             ret += to_read;
         }
@@ -84,15 +88,16 @@
 lyb_read_number(uint64_t *num, uint64_t max_num, const char *data, struct lyb_state *lybs)
 {
     int max_bits, max_bytes, i, r, ret = 0;
+    uint8_t byte;
 
     for (max_bits = 0; max_num; max_num >>= 1, ++max_bits);
     max_bytes = max_bits / 8 + (max_bits % 8 ? 1 : 0);
 
-    *num = 0;
     for (i = 0; i < max_bytes; ++i) {
-        *num <<= 8;
-        ret += (r = lyb_read(data, (uint8_t *)num, 1, lybs));
+        ret += (r = lyb_read(data, &byte, 1, lybs));
         LYB_HAVE_READ_RETURN(r, data, -1);
+
+        *(((uint8_t *)num) + i) = byte;
     }
 
     return ret;
@@ -247,7 +252,7 @@
 
     /* read anydata content */
     if (any->value_type == LYD_ANYDATA_DATATREE) {
-        any->value.tree = lyd_parse_lyb(node->schema->module->ctx, data, 0, NULL, NULL, NULL, &r);
+        any->value.tree = lyd_parse_lyb(node->schema->module->ctx, data, 0, NULL, NULL, &r);
         ret += r;
         LYB_HAVE_READ_RETURN(r, data, -1);
     } else {
@@ -261,8 +266,10 @@
 static int
 lyb_parse_val(struct lyd_node_leaf_list *node, const char *data, struct lyb_state *lybs)
 {
-    int ret;
+    int r, ret;
+    size_t i;
     uint8_t byte;
+    uint64_t num;
     struct ly_ctx *ctx = node->schema->module->ctx;
     struct lys_type *type = &((struct lys_node_leaf *)node->schema)->type;
 
@@ -292,7 +299,21 @@
         LY_CHECK_ERR_RETURN(!node->value.bit, LOGMEM(ctx), -1);
 
         /* read values */
-        /* TODO */
+        ret = 0;
+        for (i = 0; i < type->info.bits.count; ++i) {
+            if (i % 8 == 0) {
+                /* read another byte */
+                ret += (r = lyb_read(data, &byte, sizeof byte, lybs));
+                if (r < 0) {
+                    return -1;
+                }
+            }
+
+            if (byte & (0x01 << (i % 8))) {
+                /* bit is set */
+                node->value.bit[i] = &type->info.bits.bit[i];
+            }
+        }
         break;
     case LY_TYPE_BOOL:
         /* read byte */
@@ -307,9 +328,13 @@
         break;
     case LY_TYPE_ENUM:
         /* find the correct structure */
-        for (; !type->info.bits.count; type = &type->der->type);
+        for (; !type->info.enums.count; type = &type->der->type);
 
-        /* TODO */
+        num = 0;
+        ret = lyb_read_number(&num, type->info.enums.enm[type->info.enums.count - 1].value, data, lybs);
+        if ((ret > 0) && (num < type->info.enums.count)) {
+            node->value.enm = &type->info.enums.enm[num];
+        }
         break;
     case LY_TYPE_INT8:
     case LY_TYPE_UINT8:
@@ -336,11 +361,12 @@
 }
 
 static int
-lyb_parse_val_str(struct lyd_node_leaf_list *node)
+lyb_parse_val_str(struct lyd_node_leaf_list *node, struct unres_data *unres)
 {
     struct ly_ctx *ctx = node->schema->module->ctx;
     struct lys_type *type = &((struct lys_node_leaf *)node->schema)->type;
-    char num_str[21];
+    char num_str[22], *str;
+    uint32_t i, str_len;
 
     if (node->value_flags & LY_VALUE_UNRES) {
         /* nothing to do */
@@ -363,8 +389,10 @@
 
     switch (node->value_type) {
     case LY_TYPE_INST:
-        /* fill the instance-identifier target now */
-        /* TODO */
+        /* try to fill the instance-identifier target now */
+        if (unres_data_add(unres, (struct lyd_node *)node, UNRES_INSTID)) {
+            return -1;
+        }
         break;
     case LY_TYPE_IDENT:
         /* fill the identity pointer now */
@@ -380,7 +408,25 @@
         node->value_str = node->value.string;
         break;
     case LY_TYPE_BITS:
-        /* TODO */
+        for (; !type->info.bits.count; type = &type->der->type);
+
+        /* print the set bits */
+        str = malloc(1);
+        LY_CHECK_ERR_RETURN(!str, LOGMEM(ctx), -1);
+        str[0] = '\0';
+        str_len = 1;
+        for (i = 0; i < type->info.bits.count; ++i) {
+            if (node->value.bit[i]) {
+                str = ly_realloc(str, str_len + strlen(node->value.bit[i]->name) + 1);
+                LY_CHECK_ERR_RETURN(!str, LOGMEM(ctx), -1);
+
+                sprintf(str + str_len, "%s%s", str_len == 1 ? "" : " ", node->value.bit[i]->name);
+
+                str_len += strlen(node->value.bit[i]->name) + 1;
+            }
+        }
+
+        node->value_str = lydict_insert_zc(ctx, str);
         break;
     case LY_TYPE_BOOL:
         node->value_str = lydict_insert(ctx, (node->value.bln ? "true" : "false"), 0);
@@ -390,7 +436,8 @@
         /* leave value empty */
         break;
     case LY_TYPE_ENUM:
-        /* TODO */
+        /* print the value */
+        node->value_str = lydict_insert(ctx, node->value.enm->name, 0);
         break;
     case LY_TYPE_INT8:
         sprintf(num_str, "%d", node->value.int8);
@@ -425,7 +472,8 @@
         node->value_str = lydict_insert(ctx, num_str, 0);
         break;
     case LY_TYPE_DEC64:
-        /* TODO */
+        sprintf(num_str, "%ld.%ld", node->value.dec64 / (type->info.dec64.dig * 10), node->value.dec64 % (type->info.dec64.dig * 10));
+        node->value_str = lydict_insert(ctx, num_str, 0);
         break;
     default:
         return -1;
@@ -435,7 +483,7 @@
 }
 
 static int
-lyb_parse_leaf(struct lyd_node *node, const char *data, struct lyb_state *lybs)
+lyb_parse_leaf(struct lyd_node *node, const char *data, struct unres_data *unres, struct lyb_state *lybs)
 {
     int r, ret = 0;
     uint8_t start_byte;
@@ -460,19 +508,34 @@
     ret += (r = lyb_parse_val(leaf, data, lybs));
     LYB_HAVE_READ_RETURN(r, data, -1);
 
-    ret += (r = lyb_parse_val_str(leaf));
+    ret += (r = lyb_parse_val_str(leaf, unres));
     LYB_HAVE_READ_RETURN(r, data, -1);
 
     return ret;
 }
 
 static int
-lyb_parse_schema_hash(const struct lys_node *sparent, const struct lys_module *mod, const char *data,
-                      struct hash_table **sibling_ht, struct lys_node **snode, struct lyb_state *lybs)
+lyb_is_schema_hash_match(struct lys_node *sibling, LYB_HASH hash)
+{
+    LYB_HASH sibling_hash;
+    uint8_t collision_id;
+
+    /* get collision ID */
+    for (collision_id = 0; !(hash & (LYB_HASH_COLLISION_ID >> collision_id)); ++collision_id);
+
+    /* get correct collision ID node hash */
+    sibling_hash = lyb_hash(sibling, collision_id);
+
+    return hash == sibling_hash;
+}
+
+static int
+lyb_parse_schema_hash(const struct lys_node *sparent, const struct lys_module *mod, const char *data, const char *yang_data_name,
+                      int options, struct lys_node **snode, struct lyb_state *lybs)
 {
     int r, ret = 0;
-    const struct lys_node *sibling = NULL;
-    LYB_HASH hash, cur_hash;
+    struct lys_node *sibling = NULL;
+    LYB_HASH hash = 0;
     struct ly_ctx *ctx;
 
     assert((sparent || mod) && (!sparent || !mod));
@@ -481,37 +544,20 @@
     /* read the hash */
     ret += (r = lyb_read(data, &hash, sizeof hash, lybs));
     LYB_HAVE_READ_RETURN(r, data, -1);
+    if (!hash) {
+        return -1;
+    }
 
-    while ((sibling = lys_getnext(sibling, sparent, mod, 0))) {
-
-        /* make sure hashes are ready and get the current one */
-        cur_hash = 0;
-#ifdef LY_ENABLED_CACHE
-        if (sibling->hash) {
-            cur_hash = sibling->hash;
-        } else
-#endif
-        if (!*sibling_ht) {
-            *sibling_ht = lyb_hash_siblings((struct lys_node *)sibling);
-            if (!*sibling_ht) {
-                return -1;
-            }
-        }
-        if (!cur_hash) {
-            cur_hash = lyb_hash_find(*sibling_ht, sibling);
-            if (!cur_hash) {
-                return -1;
-            }
-        }
-
-        if (hash == cur_hash) {
+    while ((sibling = (struct lys_node *)lys_getnext(sibling, sparent, mod, 0))) {
+        /* skip schema nodes from models not present during printing */
+        if (lyb_has_schema_model(sibling, lybs->models, lybs->mod_count) && lyb_is_schema_hash_match(sibling, hash)) {
             /* match found */
-            *snode = (struct lys_node *)sibling;
             break;
         }
     }
 
-    if (!sibling) {
+    *snode = sibling;
+    if (!sibling && (options & LYD_OPT_STRICT)) {
         if (mod) {
             LOGVAL(ctx, LYE_SPEC, LY_VLOG_NONE, NULL, "Failed to find matching hash for a top-level node from \"%s\".", mod->name);
         } else {
@@ -525,10 +571,9 @@
 
 static int
 lyb_parse_subtree(struct ly_ctx *ctx, const char *data, struct lyd_node *parent, struct lyd_node **first_sibling,
-                  struct hash_table **sibling_ht, struct lyb_state *lybs)
+                  const char *yang_data_name, int options, struct unres_data *unres, struct lyb_state *lybs)
 {
     int r, ret = 0;
-    struct hash_table *children_ht = NULL;
     struct lyd_node *node = NULL, *iter;
     const struct lys_module *mod;
     struct lys_node *sparent, *snode;
@@ -551,9 +596,15 @@
     }
 
     /* read hash, find the schema node */
-    ret += (r = lyb_parse_schema_hash(sparent, mod, data, sibling_ht, &snode, lybs));
+    ret += (r = lyb_parse_schema_hash(sparent, mod, data, yang_data_name, options, &snode, lybs));
     LYB_HAVE_READ_GOTO(r, data, error);
 
+    if (!snode) {
+        /* unknown data subtree, skip it */
+        ret += (r = lyb_read(data, NULL, lybs->written[lybs->used - 1], lybs));
+        goto stop_subtree;
+    }
+
     /*
      * read the node
      */
@@ -564,8 +615,6 @@
 
     /* TODO read attributes */
 
-    /* TODO read hash if flag */
-
     /* read node content */
     switch (snode->nodetype) {
     case LYS_CONTAINER:
@@ -574,7 +623,7 @@
         break;
     case LYS_LEAF:
     case LYS_LEAFLIST:
-        ret += (r = lyb_parse_leaf(node, data, lybs));
+        ret += (r = lyb_parse_leaf(node, data, unres, lybs));
         LYB_HAVE_READ_GOTO(r, data, error);
         break;
     case LYS_ANYXML:
@@ -610,15 +659,9 @@
 
     /* read all descendants */
     while (lybs->written[lybs->used - 1]) {
-        ret += (r = lyb_parse_subtree(ctx, data, node, NULL, &children_ht, lybs));
+        ret += (r = lyb_parse_subtree(ctx, data, node, NULL, NULL, options, unres, lybs));
         LYB_HAVE_READ_GOTO(r, data, error);
     }
-    if (children_ht) {
-        lyht_free(children_ht);
-    }
-
-    /* end the subtree */
-    lyb_read_stop_subtree(lybs);
 
     /* make containers default if should be */
     if (node->schema->nodetype == LYS_CONTAINER) {
@@ -633,12 +676,13 @@
         }
     }
 
+stop_subtree:
+    /* end the subtree */
+    lyb_read_stop_subtree(lybs);
+
     return ret;
 
 error:
-    if (children_ht) {
-        lyht_free(children_ht);
-    }
     lyd_free(node);
     if (*first_sibling == node) {
         *first_sibling = NULL;
@@ -647,26 +691,45 @@
 }
 
 static int
+lyb_parse_data_models(struct ly_ctx *ctx, const char *data, struct lyb_state *lybs)
+{
+    int i, r, ret = 0;
+
+    /* read model count */
+    ret += (r = lyb_read(data, (uint8_t *)&lybs->mod_count, 2, lybs));
+    LYB_HAVE_READ_RETURN(r, data, -1);
+
+    lybs->models = malloc(lybs->mod_count * sizeof *lybs->models);
+    LY_CHECK_ERR_RETURN(!lybs->models, LOGMEM(NULL), -1);
+
+    /* read modules */
+    for (i = 0; i < lybs->mod_count; ++i) {
+        ret += (r = lyb_parse_model(ctx, data, &lybs->models[i], lybs));
+        LYB_HAVE_READ_RETURN(r, data, -1);
+    }
+
+    return ret;
+}
+
+static int
 lyb_parse_header(const char *data, struct lyb_state *lybs)
 {
     int ret = 0;
     uint8_t byte = 0;
 
-    /* TODO version, option for hash storing */
+    /* TODO version, any flags? */
     ret += lyb_read(data, (uint8_t *)&byte, sizeof byte, lybs);
 
-    /* TODO all used models */
-
     return ret;
 }
 
 struct lyd_node *
-lyd_parse_lyb(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *rpc_act,
-              const struct lyd_node *data_tree, const char *yang_data_name, int *parsed)
+lyd_parse_lyb(struct ly_ctx *ctx, const char *data, int options, const struct lyd_node *data_tree,
+              const char *yang_data_name, int *parsed)
 {
     int r, ret = 0;
-    struct hash_table *top_sibling_ht = NULL;
-    struct lyd_node *node = NULL;
+    struct lyd_node *node = NULL, *next, *act_notif = NULL;
+    struct unres_data *unres = NULL;
     struct lyb_state lybs;
 
     if (!ctx || !data) {
@@ -679,29 +742,70 @@
     LY_CHECK_ERR_GOTO(!lybs.written || !lybs.position, LOGMEM(ctx), finish);
     lybs.used = 0;
     lybs.size = LYB_STATE_STEP;
+    lybs.models = NULL;
+    lybs.mod_count = 0;
+
+    unres = calloc(1, sizeof *unres);
+    LY_CHECK_ERR_GOTO(!unres, LOGMEM(ctx), finish);
 
     /* read header */
     ret += (r = lyb_parse_header(data, &lybs));
     LYB_HAVE_READ_GOTO(r, data, finish);
 
-    /* TODO read used models */
+    /* read used models */
+    ret += (r = lyb_parse_data_models(ctx, data, &lybs));
+    LYB_HAVE_READ_GOTO(r, data, finish);
 
     /* read subtree(s) */
     while (data[0]) {
-        ret += (r = lyb_parse_subtree(ctx, data, NULL, &node, &top_sibling_ht, &lybs));
-        LYB_HAVE_READ_GOTO(r, data, finish);
+        ret += (r = lyb_parse_subtree(ctx, data, NULL, &node, yang_data_name, options, unres, &lybs));
+        if (r < 0) {
+            lyd_free_withsiblings(node);
+            node = NULL;
+            goto finish;
+        }
+        data += r;
     }
 
     /* read the last zero, parsing finished */
     ++ret;
     r = ret;
 
-finish:
-    if (top_sibling_ht) {
-        lyht_free(top_sibling_ht);
+    if (options & LYD_OPT_DATA_ADD_YANGLIB) {
+        if (lyd_merge(node, ly_ctx_info(ctx), LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) {
+            LOGERR(ctx, LY_EINT, "Adding ietf-yang-library data failed.");
+            lyd_free_withsiblings(node);
+            node = NULL;
+            goto finish;
+        }
     }
+
+    /* resolve any unresolved instance-identifiers */
+    if (unres->count) {
+        if (options & (LYD_OPT_RPC | LYD_OPT_RPCREPLY | LYD_OPT_NOTIF)) {
+            LY_TREE_DFS_BEGIN(node, next, act_notif) {
+                if (act_notif->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+                    break;
+                }
+                LY_TREE_DFS_END(node, next, act_notif);
+            }
+        }
+        if (lyd_defaults_add_unres(&node, options, ctx, data_tree, act_notif, unres, 0)) {
+            lyd_free_withsiblings(node);
+            node = NULL;
+            goto finish;
+        }
+    }
+
+finish:
     free(lybs.written);
     free(lybs.position);
+    free(lybs.models);
+    if (unres) {
+        free(unres->node);
+        free(unres->type);
+        free(unres);
+    }
 
     if (parsed) {
         *parsed = r;
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 1898985..52fdbf6 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -748,7 +748,7 @@
     ly_set_free(set);
 
     /* add default values, resolve unres and check for mandatory nodes in final tree */
-    if (lyd_defaults_add_unres(&result, options, ctx, data_tree, act_notif, unres)) {
+    if (lyd_defaults_add_unres(&result, options, ctx, data_tree, act_notif, unres, 1)) {
         goto error;
     }
     if (!(options & (LYD_OPT_TRUSTED | LYD_OPT_NOTIF_FILTER))
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index 3707b97..a0c8a3b 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -128,10 +128,8 @@
     max_bytes = max_bits / 8 + (max_bits % 8 ? 1 : 0);
 
     for (i = 0; i < max_bytes; ++i) {
-        byte = (uint8_t)num;
+        byte = *(((uint8_t *)&num) + i);
         ret += lyb_write(out, &byte, 1, lybs);
-
-        num >>= 8;
     }
 
     return ret;
@@ -194,12 +192,12 @@
 }
 
 static int
-is_added_module(const struct lys_module **modules, size_t mod_count, const struct lys_module *mod)
+is_added_model(const struct lys_module **models, size_t mod_count, const struct lys_module *mod)
 {
     size_t i;
 
     for (i = 0; i < mod_count; ++i) {
-        if (modules[i] == mod) {
+        if (models[i] == mod) {
             return 1;
         }
     }
@@ -208,21 +206,21 @@
 }
 
 static void
-add_module(const struct lys_module ***modules, size_t *mod_count, const struct lys_module *mod)
+add_model(const struct lys_module ***models, size_t *mod_count, const struct lys_module *mod)
 {
-    if (is_added_module(*modules, *mod_count, mod)) {
+    if (is_added_model(*models, *mod_count, mod)) {
         return;
     }
 
-    *modules = ly_realloc(*modules, ++(*mod_count) * sizeof **modules);
-    (*modules)[*mod_count - 1] = mod;
+    *models = ly_realloc(*models, ++(*mod_count) * sizeof **models);
+    (*models)[*mod_count - 1] = mod;
 }
 
 static int
 lyb_print_data_models(struct lyout *out, const struct lyd_node *root, struct lyb_state *lybs)
 {
     int ret = 0;
-    const struct lys_module **modules = NULL, *mod;
+    const struct lys_module **models = NULL, *mod;
     const struct lyd_node *node;
     size_t mod_count = 0;
     uint32_t idx = 0, i;
@@ -230,7 +228,7 @@
     /* first, collect all data node modules */
     LY_TREE_FOR(root, node) {
         mod = lyd_node_module(node);
-        add_module(&modules, &mod_count, mod);
+        add_model(&models, &mod_count, mod);
     }
 
     /* then add all models augmenting or deviating the used models */
@@ -242,28 +240,28 @@
         }
 
         for (i = 0; i < mod->deviation_size; ++i) {
-            if (mod->deviation[i].orig_node && is_added_module(modules, mod_count, lys_node_module(mod->deviation[i].orig_node))) {
-                add_module(&modules, &mod_count, mod);
+            if (mod->deviation[i].orig_node && is_added_model(models, mod_count, lys_node_module(mod->deviation[i].orig_node))) {
+                add_model(&models, &mod_count, mod);
                 goto next_mod;
             }
         }
         for (i = 0; i < mod->augment_size; ++i) {
-            if (is_added_module(modules, mod_count, lys_node_module(mod->augment[i].target))) {
-                add_module(&modules, &mod_count, mod);
+            if (is_added_model(models, mod_count, lys_node_module(mod->augment[i].target))) {
+                add_model(&models, &mod_count, mod);
                 goto next_mod;
             }
         }
     }
 
     /* now write module count on 2 bytes */
-    ret += lyb_write(out, (uint8_t *)mod_count, 2, lybs);
+    ret += lyb_write(out, (uint8_t *)&mod_count, 2, lybs);
 
     /* and all the used models */
     for (i = 0; i < mod_count; ++i) {
-        ret += lyb_print_model(out, modules[i], lybs);
+        ret += lyb_print_model(out, models[i], lybs);
     }
 
-    free(modules);
+    free(models);
     return ret;
 }
 
@@ -273,11 +271,9 @@
     int ret = 0;
     uint8_t byte = 0;
 
-    /* TODO version, option for hash storing */
+    /* TODO version, some other flags? */
     ret += ly_write(out, (char *)&byte, sizeof byte);
 
-    /* TODO all used models */
-
     return ret;
 }
 
@@ -435,8 +431,9 @@
         /* find the correct structure */
         for (; !type->info.enums.count; type = &type->der->type);
 
-        /* store the value (save bytes if possible) */
-        ret += lyb_write_number(leaf->value.enm->value, type->info.enums.enm[type->info.enums.count - 1].value, out, lybs);
+        /* store the enum index (save bytes if possible) */
+        i = (leaf->value.enm - type->info.enums.enm) / sizeof *leaf->value.enm;
+        ret += lyb_write_number(i, type->info.enums.enm[type->info.enums.count - 1].value, out, lybs);
         break;
     case LY_TYPE_INT8:
     case LY_TYPE_UINT8:
@@ -462,6 +459,31 @@
     return ret;
 }
 
+static LYB_HASH
+lyb_hash_find(struct hash_table *ht, struct lys_node *node)
+{
+    LYB_HASH hash;
+    uint32_t i;
+
+    for (i = 0; i < LYB_HASH_BITS; ++i) {
+        hash = lyb_hash(node, i);
+        if (!hash) {
+            return 0;
+        }
+
+        if (!lyht_find(ht, &node, hash, NULL)) {
+            /* success, no collision */
+            break;
+        }
+    }
+    /* cannot happen, we already calculated the hash */
+    if (i > LYB_HASH_BITS) {
+        return 0;
+    }
+
+    return hash;
+}
+
 static int
 lyb_print_subtree(struct lyout *out, const struct lyd_node *node, struct hash_table **sibling_ht, struct lyb_state *lybs,
                   int options, int top_level)
@@ -470,23 +492,16 @@
     LYB_HASH hash = 0;
     struct hash_table *children_ht = NULL;
 
-    /* get the correct node hash, create whole sibling HT if needed */
-#ifdef LY_ENABLED_CACHE
-    if (node->schema->hash) {
-        hash = node->schema->hash;
-    } else
-#endif
+    /* create whole sibling HT if not already and get our hash */
     if (!*sibling_ht) {
-        *sibling_ht = lyb_hash_siblings(node->schema);
+        *sibling_ht = lyb_hash_siblings(node->schema, NULL, 0);
         if (!*sibling_ht) {
             return -1;
         }
     }
+    hash = lyb_hash_find(*sibling_ht, node->schema);
     if (!hash) {
-        hash = lyb_hash_find(*sibling_ht, node->schema);
-        if (!hash) {
-            return -1;
-        }
+        return -1;
     }
 
     /* register a new subtree */
@@ -515,8 +530,6 @@
 
     /* TODO write attributes */
 
-    /* TODO write hash if flag */
-
     /* write node content */
     switch (node->schema->nodetype) {
     case LYS_CONTAINER:
@@ -570,7 +583,7 @@
 int
 lyb_print_data(struct lyout *out, const struct lyd_node *root, int options)
 {
-    int ret = 0;
+    int r, ret = 0;
     uint8_t zero = 0;
     struct hash_table *top_sibling_ht = NULL;
     struct lyb_state lybs;
@@ -582,16 +595,36 @@
     lybs.size = LYB_STATE_STEP;
 
     /* LYB header */
-    ret += lyb_print_header(out, options);
-
-    /* all used models */
-    ret += lyb_print_data_models(out, root, &lybs);
-
-    LY_TREE_FOR(root, root) {
-        ret += lyb_print_subtree(out, root, &top_sibling_ht, &lybs, options, 1);
+    ret += (r = lyb_print_header(out, options));
+    if (r < 0) {
+        ret = -1;
+        goto finish;
     }
 
-    ret += lyb_write(out, &zero, sizeof zero, &lybs);
+    /* all used models */
+    ret += (r = lyb_print_data_models(out, root, &lybs));
+    if (r < 0) {
+        ret = -1;
+        goto finish;
+    }
+
+    LY_TREE_FOR(root, root) {
+        ret += (r = lyb_print_subtree(out, root, &top_sibling_ht, &lybs, options, 1));
+        if (r < 0) {
+            ret = -1;
+            goto finish;
+        }
+
+        if (!(options & LYP_WITHSIBLINGS)) {
+            break;
+        }
+    }
+
+    /* ending zero byte */
+    ret += (r = lyb_write(out, &zero, sizeof zero, &lybs));
+    if (r < 0) {
+        ret = -1;
+    }
 
 finish:
     if (top_sibling_ht) {
diff --git a/src/tree_data.c b/src/tree_data.c
index ba12a72..cabfab8 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -924,7 +924,7 @@
         result = lyd_parse_json(ctx, data, options, rpc_act, data_tree, yang_data_name);
         break;
     case LYD_LYB:
-        result = lyd_parse_lyb(ctx, data, options, rpc_act, data_tree, yang_data_name, NULL);
+        result = lyd_parse_lyb(ctx, data, options, data_tree, yang_data_name, NULL);
         break;
     default:
         /* error */
@@ -5006,7 +5006,7 @@
     }
 
     /* add default values, resolve unres and check for mandatory nodes in final tree */
-    if (lyd_defaults_add_unres(node, options, ctx, data_tree, act_notif, unres)) {
+    if (lyd_defaults_add_unres(node, options, ctx, data_tree, act_notif, unres, 1)) {
         goto cleanup;
     }
     if (act_notif) {
@@ -7017,24 +7017,9 @@
     return EXIT_SUCCESS;
 }
 
-/**
- * @brief Process (add/clean) default nodes in the data tree and resolve the unresolved items
- *
- * @param[in,out] root  Pointer to the root node of the complete data tree, the root node can be NULL if the data tree
- *                      is empty
- * @param[in] options   Parser options to know the data tree type, see @ref parseroptions.
- * @param[in] ctx       Context for the case the \p root is empty (in that case \p ctx must not be NULL)
- * @param[in] data_tree Additional data tree for validating RPC/action/notification. The tree is used to satisfy
- *                      possible references to the datastore content.
- * @param[in] act_notif In case of nested action/notification, pointer to the subroot of the action/notification. Note
- *                      that in this case the \p root points to the top level data tree node which provides the context
- *                      for the nested action/notification
- * @param[in] unres     Unresolved data list, the newly added default nodes may need to add some unresolved items
- * @return EXIT_SUCCESS or EXIT_FAILURE
- */
 int
 lyd_defaults_add_unres(struct lyd_node **root, int options, struct ly_ctx *ctx, const struct lyd_node *data_tree,
-                       struct lyd_node *act_notif, struct unres_data *unres)
+                       struct lyd_node *act_notif, struct unres_data *unres, int wd)
 {
     struct lyd_node *msg_sibling = NULL, *msg_parent = NULL, *data_tree_sibling, *data_tree_parent;
     struct lys_node *msg_op = NULL;
@@ -7080,7 +7065,7 @@
     }
 
     /* add missing default nodes */
-    if (lyd_wd_add((act_notif ? &act_notif : root), ctx, unres, options)) {
+    if (wd && lyd_wd_add((act_notif ? &act_notif : root), ctx, unres, options)) {
         return EXIT_FAILURE;
     }
 
diff --git a/src/tree_data.h b/src/tree_data.h
index 97d249d..057ce95 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -530,7 +530,7 @@
  * @param[in] ctx Context to connect with the data tree being built here.
  * @param[in] data Serialized data in the specified format.
  * @param[in] format Format of the input data to be parsed.
- * @param[in] options Parser options, see @ref parseroptions.
+ * @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
  * @param[in] ... Variable arguments depend on \p options. If they include:
  *                - #LYD_OPT_DATA:
  *                - #LYD_OPT_CONFIG:
@@ -568,7 +568,7 @@
  * @param[in] ctx Context to connect with the data tree being built here.
  * @param[in] fd The standard file descriptor of the file containing the data tree in the specified format.
  * @param[in] format Format of the input data to be parsed.
- * @param[in] options Parser options, see @ref parseroptions.
+ * @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
  * @param[in] ... Variable arguments depend on \p options. If they include:
  *                - #LYD_OPT_DATA:
  *                - #LYD_OPT_CONFIG:
@@ -604,7 +604,7 @@
  * @param[in] ctx Context to connect with the data tree being built here.
  * @param[in] path Path to the file containing the data tree in the specified format.
  * @param[in] format Format of the input data to be parsed.
- * @param[in] options Parser options, see @ref parseroptions.
+ * @param[in] options Parser options, see @ref parseroptions. \p format LYD_LYB uses #LYD_OPT_TRUSTED implicitly.
  * @param[in] ... Variable arguments depend on \p options. If they include:
  *                - #LYD_OPT_DATA:
  *                - #LYD_OPT_CONFIG:
@@ -1240,14 +1240,11 @@
 /**
 * @brief Print data tree in the specified format.
 *
-* Same as lyd_print(), but it allocates memory and store the data into it.
-* It is up to caller to free the returned string by free().
-*
 * @param[out] strp Pointer to store the resulting dump.
 * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
 * node of the data tree to print the specific subtree.
 * @param[in] format Data output format.
-* @param[in] options [printer flags](@ref printerflags).
+* @param[in] options [printer flags](@ref printerflags). \p format LYD_LYB accepts only #LYP_WITHSIBLINGS option.
 * @return 0 on success, 1 on failure (#ly_errno is set).
 */
 int lyd_print_mem(char **strp, const struct lyd_node *root, LYD_FORMAT format, int options);
@@ -1255,13 +1252,11 @@
 /**
  * @brief Print data tree in the specified format.
  *
- * Same as lyd_print(), but output is written into the specified file descriptor.
- *
  * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
  * node of the data tree to print the specific subtree.
  * @param[in] fd File descriptor where to print the data.
  * @param[in] format Data output format.
- * @param[in] options [printer flags](@ref printerflags).
+ * @param[in] options [printer flags](@ref printerflags). \p format LYD_LYB accepts only #LYP_WITHSIBLINGS option.
  * @return 0 on success, 1 on failure (#ly_errno is set).
  */
 int lyd_print_fd(int fd, const struct lyd_node *root, LYD_FORMAT format, int options);
@@ -1269,13 +1264,11 @@
 /**
  * @brief Print data tree in the specified format.
  *
- * To write data into a file descriptor, use lyd_print_fd().
- *
  * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
  * node of the data tree to print the specific subtree.
  * @param[in] f File stream where to print the data.
  * @param[in] format Data output format.
- * @param[in] options [printer flags](@ref printerflags).
+ * @param[in] options [printer flags](@ref printerflags). \p format LYD_LYB accepts only #LYP_WITHSIBLINGS option.
  * @return 0 on success, 1 on failure (#ly_errno is set).
  */
 int lyd_print_file(FILE *f, const struct lyd_node *root, LYD_FORMAT format, int options);
@@ -1283,14 +1276,12 @@
 /**
  * @brief Print data tree in the specified format.
  *
- * Same as lyd_print(), but output is written via provided callback.
- *
  * @param[in] root Root node of the data tree to print. It can be actually any (not only real root)
  * node of the data tree to print the specific subtree.
  * @param[in] writeclb Callback function to write the data (see write(1)).
  * @param[in] arg Optional caller-specific argument to be passed to the \p writeclb callback.
  * @param[in] format Data output format.
- * @param[in] options [printer flags](@ref printerflags).
+ * @param[in] options [printer flags](@ref printerflags). \p format LYD_LYB accepts only #LYP_WITHSIBLINGS option.
  * @return 0 on success, 1 on failure (#ly_errno is set).
  */
 int lyd_print_clb(ssize_t (*writeclb)(void *arg, const void *buf, size_t count), void *arg,
diff --git a/src/tree_internal.h b/src/tree_internal.h
index 063742c..931a1a4 100644
--- a/src/tree_internal.h
+++ b/src/tree_internal.h
@@ -70,6 +70,8 @@
     size_t *position;
     int used;
     int size;
+    const struct lys_module **models;
+    int mod_count;
 };
 
 /* struct lyb_state allocation step */
@@ -104,11 +106,11 @@
 /* Maximum size that will be written into LYB_SIZE_BYTES (must be large enough) */
 #define LYB_SIZE_MAX 255
 
-LYB_HASH lyb_hash(const struct lys_node *sibling, uint8_t collision_id);
+LYB_HASH lyb_hash(struct lys_node *sibling, uint8_t collision_id);
 
-struct hash_table *lyb_hash_siblings(struct lys_node *sibling);
+int lyb_has_schema_model(struct lys_node *sibling, const struct lys_module **models, int mod_count);
 
-LYB_HASH lyb_hash_find(struct hash_table *ht, const struct lys_node *node);
+struct hash_table *lyb_hash_siblings(struct lys_node *sibling, const struct lys_module **models, int mod_count);
 
 /**
  * Macros to work with ::lyd_node#when_status
@@ -508,22 +510,23 @@
 int lys_ingrouping(const struct lys_node *node);
 
 /**
- * @brief Add default values, \p resolve unres, and finally
- * remove any redundant default values based on \p options.
+ * @brief Process (add/clean) default nodes in the data tree and resolve the unresolved items
  *
- * @param[in] root Data tree root. With empty data tree, new default nodes can be created so the root pointer
- *            will contain/return the newly created data tree.
- * @param[in] options Options for the inserting data to the target data tree options, see @ref parseroptions.
- * @param[in] ctx Optional parameter. If provided, default nodes from all modules in the context will be added.
- *            If NULL, only the modules explicitly mentioned in data tree are taken into account.
- * @param[in] data_tree Additional data tree to be traversed when evaluating when or must expressions in \p root
- *            tree.
- * @param[in] act_notif Action/notification itself in case \p root is actually an action/notification.
- * @param[in] unres Valid unres structure, on function successful exit they are all resolved.
- * @return 0 on success, nonzero on failure.
+ * @param[in,out] root  Pointer to the root node of the complete data tree, the root node can be NULL if the data tree
+ *                      is empty
+ * @param[in] options   Parser options to know the data tree type, see @ref parseroptions.
+ * @param[in] ctx       Context for the case the \p root is empty (in that case \p ctx must not be NULL)
+ * @param[in] data_tree Additional data tree for validating RPC/action/notification. The tree is used to satisfy
+ *                      possible references to the datastore content.
+ * @param[in] act_notif In case of nested action/notification, pointer to the subroot of the action/notification. Note
+ *                      that in this case the \p root points to the top level data tree node which provides the context
+ *                      for the nested action/notification
+ * @param[in] unres     Unresolved data list, the newly added default nodes may need to add some unresolved items
+ * @param[in] wd        Whether to add default values.
+ * @return EXIT_SUCCESS or EXIT_FAILURE
  */
 int lyd_defaults_add_unres(struct lyd_node **root, int options, struct ly_ctx *ctx, const struct lyd_node *data_tree,
-                           struct lyd_node *act_notif, struct unres_data *unres);
+                           struct lyd_node *act_notif, struct unres_data *unres, int wd);
 
 void lys_enable_deviations(struct lys_module *module);
 
diff --git a/src/tree_schema.h b/src/tree_schema.h
index e7c47fa..6f722ac 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1210,6 +1210,15 @@
  * @}
  */
 
+#ifdef LY_ENABLED_CACHE
+
+/**
+ * @brief Maximum number of hashes stored in a schema node if cache is enabled.
+ */
+#define LYS_NODE_HASH_COUNT 4
+
+#endif
+
 /**
  * @brief Common structure representing single YANG data statement describing.
  *
@@ -1256,7 +1265,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 } _PACKED;
 
@@ -1298,7 +1307,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific container's data */
@@ -1389,7 +1398,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific leaf's data */
@@ -1443,7 +1452,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific leaf-list's data */
@@ -1498,7 +1507,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific list's data */
@@ -1555,7 +1564,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific anyxml's data */
@@ -1773,7 +1782,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific rpc's data */
@@ -1819,7 +1828,7 @@
     void *priv;                      /**< private caller's data, not used by libyang */
 
 #ifdef LY_ENABLED_CACHE
-    uint8_t hash;                    /**< schema hash required for LYB printer/parser */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
 #endif
 
     /* specific rpc's data */