data tree FEATURE binary LYB printer and parser
diff --git a/src/lyb.h b/src/lyb.h
new file mode 100644
index 0000000..e5bf309
--- /dev/null
+++ b/src/lyb.h
@@ -0,0 +1,136 @@
+/**
+ * @file lyb.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for LYB format printer & parser
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_LYB_H_
+#define LY_LYB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "set.h"
+#include "tree.h"
+
+struct hash_table;
+struct ly_ctx;
+struct lyd_node;
+struct lysc_node;
+
+/**
+ * @brief Internal structure for LYB parser/printer.
+ */
+struct lyd_lyb_ctx {
+    struct lyd_lyb_subtree {
+        size_t written;
+        size_t position;
+        uint8_t inner_chunks;
+    } *subtrees;
+    LY_ARRAY_SIZE_TYPE subtree_size;
+
+    size_t byte_count;  /**< printed/parsed bytes */
+    const struct ly_ctx *ctx;
+    int options;
+
+    /* LYB parser only */
+    const char *data;
+    int int_opts;
+    const struct lys_module **models;
+    struct ly_set unres_node_type;
+    struct ly_set unres_meta_type;
+    struct ly_set when_check;
+    struct lyd_node *op_ntf;
+
+    /* LYB printer only */
+    struct lyd_lyb_sib_ht {
+        struct lysc_node *first_sibling;
+        struct hash_table *ht;
+    } *sib_hts;
+};
+
+/**
+ * LYB format
+ *
+ * Unlike XML or JSON, it is binary format so most data are represented in similar way but in binary.
+ * Some notable differences:
+ *
+ * - schema nodes are identified based on their hash instead of their string name. In case of collisions
+ * an array of hashes is created with each next hash one bit shorter until a unique sequence of all these
+ * hashes is found and then all of them are stored.
+ *
+ * - tree structure is represented as individual strictly bounded subtrees. Each subtree begins
+ * with its metadata, which consist of 1) the whole subtree length in bytes and 2) number
+ * of included metadata chunks of nested subtrees.
+ *
+ * - since length of a subtree is not known before it is printed, holes are first written and
+ * after the subtree is printed, they are filled with actual valid metadata. As a consequence,
+ * LYB data cannot be directly printed into streams!
+ *
+ * - data are preceded with information about all the used modules. It is needed because of
+ * possible augments and deviations which must be known beforehand, otherwise schema hashes
+ * could be matched to the wrong nodes.
+ */
+
+/* just a shortcut */
+#define LYB_LAST_SUBTREE(lybctx) lybctx->subtrees[LY_ARRAY_SIZE(lybctx->subtrees) - 1]
+
+/* struct lyd_lyb_subtree allocation step */
+#define LYB_SUBTREE_STEP 4
+
+/* current LYB format version */
+#define LYB_VERSION_NUM 0x10
+
+/* LYB format version mask of the header byte */
+#define LYB_VERSION_MASK 0x10
+
+/**
+ * LYB schema hash constants
+ *
+ * Hash is divided to collision ID and hash itself.
+ *
+ * First bits are collision ID until 1 is found. The rest is truncated 32b hash.
+ * 1xxx xxxx - collision ID 0 (no collisions)
+ * 01xx xxxx - collision ID 1 (collision ID 0 hash collided)
+ * 001x xxxx - collision ID 2 ...
+ */
+
+/* Number of bits the whole hash will take (including hash collision ID) */
+#define LYB_HASH_BITS 8
+
+/* Masking 32b hash (collision ID 0) */
+#define LYB_HASH_MASK 0x7f
+
+/* Type for storing the whole hash (used only internally, publicly defined directly) */
+#define LYB_HASH uint8_t
+
+/* Need to move this first >> collision number (from 0) to get collision ID hash part */
+#define LYB_HASH_COLLISION_ID 0x80
+
+/* How many bytes are reserved for one data chunk SIZE (8B is maximum) */
+#define LYB_SIZE_BYTES 1
+
+/* Maximum size that will be written into LYB_SIZE_BYTES (must be large enough) */
+#define LYB_SIZE_MAX UINT8_MAX
+
+/* How many bytes are reserved for one data chunk inner chunk count */
+#define LYB_INCHUNK_BYTES 1
+
+/* Maximum size that will be written into LYB_INCHUNK_BYTES (must be large enough) */
+#define LYB_INCHUNK_MAX UINT8_MAX
+
+/* Just a helper macro */
+#define LYB_META_BYTES (LYB_INCHUNK_BYTES + LYB_SIZE_BYTES)
+
+/* Type large enough for all meta data */
+#define LYB_META uint16_t
+
+#endif /* LY_LYB_H_ */
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
new file mode 100644
index 0000000..2710751
--- /dev/null
+++ b/src/parser_lyb.c
@@ -0,0 +1,1338 @@
+/**
+ * @file parser_lyb.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief LYB data parser for libyang
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "lyb.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "validation.h"
+
+/**
+ * @brief Read YANG data from LYB input. Metadata are handled transparently and not returned.
+ *
+ * @param[in] buf Destination buffer.
+ * @param[in] count Number of bytes to read.
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_read(uint8_t *buf, size_t count, struct lyd_lyb_ctx *lybctx)
+{
+    int parsed = 0;
+    LY_ARRAY_SIZE_TYPE u;
+    struct lyd_lyb_subtree *empty;
+    size_t to_read;
+    uint8_t meta_buf[LYB_META_BYTES];
+
+    assert(lybctx);
+
+    while (1) {
+        /* check for fully-read (empty) data chunks */
+        to_read = count;
+        empty = NULL;
+        LY_ARRAY_FOR(lybctx->subtrees, u) {
+            /* we want the innermost chunks resolved first, so replace previous empty chunks,
+             * also ignore chunks that are completely finished, there is nothing for us to do */
+            if ((lybctx->subtrees[u].written <= to_read) && lybctx->subtrees[u].position) {
+                /* empty chunk, do not read more */
+                to_read = lybctx->subtrees[u].written;
+                empty = &lybctx->subtrees[u];
+            }
+        }
+
+        if (!empty && !count) {
+            break;
+        }
+
+        /* we are actually reading some data, not just finishing another chunk */
+        if (to_read) {
+            if (buf) {
+                memcpy(buf, lybctx->data + parsed, to_read);
+            }
+
+            LY_ARRAY_FOR(lybctx->subtrees, u) {
+                /* decrease all written counters */
+                lybctx->subtrees[u].written -= to_read;
+                assert(lybctx->subtrees[u].written <= LYB_SIZE_MAX);
+            }
+            /* decrease count/buf */
+            count -= to_read;
+            if (buf) {
+                buf += to_read;
+            }
+
+            parsed += to_read;
+        }
+
+        if (empty) {
+            /* read the next chunk meta information */
+            memcpy(meta_buf, lybctx->data + parsed, LYB_META_BYTES);
+            empty->written = meta_buf[0];
+            empty->inner_chunks = meta_buf[1];
+
+            /* remember whether there is a following chunk or not */
+            empty->position = (empty->written == LYB_SIZE_MAX ? 1 : 0);
+
+            parsed += LYB_META_BYTES;
+        }
+    }
+
+    lybctx->byte_count += parsed;
+    lybctx->data += parsed;
+}
+
+/**
+ * @brief Read a number.
+ *
+ * @param[in] num Destination buffer.
+ * @param[in] num_size Size of @p num.
+ * @param[in] bytes Number of bytes to read.
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_read_number(void *num, size_t num_size, size_t bytes, struct lyd_lyb_ctx *lybctx)
+{
+    uint64_t buf = 0;
+
+    lyb_read((uint8_t *)&buf, bytes, lybctx);
+
+    /* correct byte order */
+    buf = le64toh(buf);
+
+    switch (num_size) {
+    case 1:
+        *((uint8_t *)num) = buf;
+        break;
+    case 2:
+        *((uint16_t *)num) = buf;
+        break;
+    case 4:
+        *((uint32_t *)num) = buf;
+        break;
+    case 8:
+        *((uint64_t *)num) = buf;
+        break;
+    default:
+        LOGINT(lybctx->ctx);
+    }
+}
+
+/**
+ * @brief Read a string.
+ *
+ * @param[in] str Destination buffer, is allocated.
+ * @param[in] with_length Whether the string is preceded with its length or it ends at the end of this subtree.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_string(char **str, int with_length, struct lyd_lyb_ctx *lybctx)
+{
+    int next_chunk = 0;
+    size_t len = 0, cur_len;
+
+    *str = NULL;
+
+    if (with_length) {
+        lyb_read_number(&len, sizeof len, 2, lybctx);
+    } else {
+        /* read until the end of this subtree */
+        len = LYB_LAST_SUBTREE(lybctx).written;
+        if (LYB_LAST_SUBTREE(lybctx).position) {
+            next_chunk = 1;
+        }
+    }
+
+    *str = malloc((len + 1) * sizeof **str);
+    LY_CHECK_ERR_RET(!*str, LOGMEM(lybctx->ctx), LY_EMEM);
+
+    lyb_read((uint8_t *)*str, len, lybctx);
+
+    while (next_chunk) {
+        cur_len = LYB_LAST_SUBTREE(lybctx).written;
+        if (LYB_LAST_SUBTREE(lybctx).position) {
+            next_chunk = 1;
+        } else {
+            next_chunk = 0;
+        }
+
+        *str = ly_realloc(*str, (len + cur_len + 1) * sizeof **str);
+        LY_CHECK_ERR_RET(!*str, LOGMEM(lybctx->ctx), LY_EMEM);
+
+        lyb_read(((uint8_t *)*str) + len, cur_len, lybctx);
+
+        len += cur_len;
+    }
+
+    ((char *)*str)[len] = '\0';
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Stop the current subtree - change LYB context state.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_stop_subtree(struct lyd_lyb_ctx *lybctx)
+{
+    if (LYB_LAST_SUBTREE(lybctx).written) {
+        LOGINT_RET(lybctx->ctx);
+    }
+
+    LY_ARRAY_DECREMENT(lybctx->subtrees);
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Start a new subtree - change LYB context state but also read the expected metadata.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_read_start_subtree(struct lyd_lyb_ctx *lybctx)
+{
+    uint8_t meta_buf[LYB_META_BYTES];
+    LY_ARRAY_SIZE_TYPE u;
+
+    if (!lybctx->subtrees) {
+        u = 0;
+    } else {
+        u = LY_ARRAY_SIZE(lybctx->subtrees);
+    }
+    if (u == lybctx->subtree_size) {
+        LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->subtrees, u + LYB_SUBTREE_STEP, LY_EMEM);
+        lybctx->subtree_size = u + LYB_SUBTREE_STEP;
+    }
+
+    memcpy(meta_buf, lybctx->data, LYB_META_BYTES);
+
+    LY_ARRAY_INCREMENT(lybctx->subtrees);
+    LYB_LAST_SUBTREE(lybctx).written = meta_buf[0];
+    LYB_LAST_SUBTREE(lybctx).inner_chunks = meta_buf[LYB_SIZE_BYTES];
+    LYB_LAST_SUBTREE(lybctx).position = (LYB_LAST_SUBTREE(lybctx).written == LYB_SIZE_MAX ? 1 : 0);
+
+    lybctx->byte_count += LYB_META_BYTES;
+    lybctx->data += LYB_META_BYTES;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse YANG model info.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[out] mod Parsed module.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_model(struct lyd_lyb_ctx *lybctx, const struct lys_module **mod)
+{
+    LY_ERR ret = LY_SUCCESS;
+    char *mod_name = NULL, mod_rev[11];
+    uint16_t rev;
+
+    /* model name */
+    ret = lyb_read_string(&mod_name, 1, lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* revision */
+    lyb_read_number(&rev, sizeof rev, 2, lybctx);
+
+    if (!mod_name[0]) {
+        /* opaq node, no module */
+        *mod = NULL;
+        goto cleanup;
+    }
+
+    if (rev) {
+        sprintf(mod_rev, "%04u-%02u-%02u", ((rev & 0xFE00) >> 9) + 2000, (rev & 0x01E0) >> 5, rev & 0x001Fu);
+        *mod = ly_ctx_get_module(lybctx->ctx, mod_name, mod_rev);
+        if ((lybctx->options & LYD_OPT_LYB_MOD_UPDATE) && !(*mod)) {
+            /* 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))) {
+                /* not an implemented module in a newer revision */
+                *mod = NULL;
+            }
+        }
+    } else {
+        *mod = 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 (lybctx->options & LYD_OPT_STRICT) {
+            if (!*mod) {
+                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) {
+                LOGERR(lybctx->ctx, LY_EINVAL, "Invalid context for LYB data parsing, module \"%s%s%s\" not implemented.",
+                    mod_name, rev ? "@" : "", rev ? mod_rev : "");
+            }
+            ret = LY_EINVAL;
+            goto cleanup;
+        }
+
+    }
+
+cleanup:
+    free(mod_name);
+    return ret;
+}
+
+/**
+ * @brief Parse YANG node metadata.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] sparent Schema parent node.
+ * @param[out] meta Parsed metadata.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_metadata(struct lyd_lyb_ctx *lybctx, const struct lysc_node *sparent, struct lyd_meta **meta)
+{
+    LY_ERR ret = LY_SUCCESS;
+    int dynamic = 0;
+    uint8_t i, count = 0;
+    char *meta_name = NULL, *meta_value = NULL;
+    const struct lys_module *mod;
+
+    /* read number of attributes stored */
+    lyb_read(&count, 1, lybctx);
+
+    /* read attributes */
+    for (i = 0; i < count; ++i) {
+        ret = lyb_read_start_subtree(lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* find model */
+        ret = lyb_parse_model(lybctx, &mod);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        if (!mod) {
+            /* skip it */
+            do {
+                lyb_read(NULL, LYB_LAST_SUBTREE(lybctx).written, lybctx);
+            } while (LYB_LAST_SUBTREE(lybctx).written);
+            goto stop_subtree;
+        }
+
+        /* meta name */
+        ret = lyb_read_string(&meta_name, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* meta value */
+        ret = lyb_read_string(&meta_value, 0, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        dynamic = 1;
+
+        /* create metadata */
+        ret = lyd_create_meta(NULL, meta, mod, meta_name, strlen(meta_name), meta_value, strlen(meta_value), &dynamic,
+                              lydjson_resolve_prefix, NULL, LYD_JSON, sparent);
+
+        /* free strings */
+        free(meta_name);
+        meta_name = NULL;
+        if (dynamic) {
+            free(meta_value);
+            dynamic = 0;
+        }
+        meta_value = NULL;
+
+        if (ret == LY_EINCOMPLETE) {
+            ly_set_add(&lybctx->unres_meta_type, *meta, LY_SET_OPT_USEASLIST);
+        } else if (ret) {
+            goto cleanup;
+        }
+
+stop_subtree:
+        ret = lyb_read_stop_subtree(lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+cleanup:
+    free(meta_name);
+    if (dynamic) {
+        free(meta_value);
+    }
+    if (ret) {
+        lyd_free_meta(lybctx->ctx, *meta, 1);
+        *meta = NULL;
+    }
+    return ret;
+}
+
+/**
+ * @brief Parse opaque prefixes structure.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[out] prefs Parsed prefixes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_opaq_prefixes(struct lyd_lyb_ctx *lybctx, struct ly_prefix **prefs)
+{
+    LY_ERR ret = LY_SUCCESS;
+    uint8_t count, i;
+    char *str;
+
+    /* read count */
+    lyb_read(&count, 1, lybctx);
+    if (!count) {
+        return LY_SUCCESS;
+    }
+
+    LY_ARRAY_CREATE_RET(lybctx->ctx, *prefs, count, LY_EMEM);
+    for (i = 0; i < count; ++i) {
+        LY_ARRAY_INCREMENT(*prefs);
+
+        /* prefix */
+        ret = lyb_read_string(&str, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        (*prefs)[i].pref = lydict_insert_zc(lybctx->ctx, str);
+
+        /* namespace */
+        ret = lyb_read_string(&str, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        (*prefs)[i].ns = lydict_insert_zc(lybctx->ctx, str);
+    }
+
+cleanup:
+    if (ret) {
+        ly_free_val_prefs(lybctx->ctx, *prefs);
+        *prefs = NULL;
+    }
+    return ret;
+}
+
+/**
+ * @brief Parse opaque attributes.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[out] attr Parsed attributes.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_attributes(struct lyd_lyb_ctx *lybctx, struct ly_attr **attr)
+{
+    LY_ERR ret = LY_SUCCESS;
+    uint8_t count, i;
+    struct ly_attr *attr2;
+    char *prefix = NULL, *ns = NULL, *name = NULL, *value = NULL;
+    int dynamic = 0;
+    LYD_FORMAT format = 0;
+    struct ly_prefix *val_prefs = NULL;
+
+    /* read count */
+    lyb_read(&count, 1, lybctx);
+
+    /* read attributes */
+    for (i = 0; i < count; ++i) {
+        ret = lyb_read_start_subtree(lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* prefix, may be emtpy */
+        ret = lyb_read_string(&prefix, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        if (!prefix[0]) {
+            free(prefix);
+            prefix = NULL;
+        }
+
+        /* namespace, may be empty */
+        ret = lyb_read_string(&ns, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        if (!ns[0]) {
+            free(ns);
+            ns = NULL;
+        }
+
+        /* name */
+        ret = lyb_read_string(&name, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* value prefixes */
+        ret = lyb_parse_opaq_prefixes(lybctx, &val_prefs);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* format */
+        lyb_read((uint8_t *)&format, 1, lybctx);
+
+        /* value */
+        ret = lyb_read_string(&value, 0, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        dynamic = 1;
+
+        /* attr2 is always changed to the created attribute */
+        ret = ly_create_attr(NULL, &attr2, lybctx->ctx, name, strlen(name), value, strlen(value), &dynamic, format,
+                             val_prefs, prefix, prefix ? strlen(prefix) : 0, ns);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        free(prefix);
+        prefix = NULL;
+        free(ns);
+        ns = NULL;
+        free(name);
+        name = NULL;
+        val_prefs = NULL;
+        assert(!dynamic);
+        value = NULL;
+
+        if (!*attr) {
+            *attr = attr2;
+        }
+
+        ret = lyb_read_stop_subtree(lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+cleanup:
+    free(prefix);
+    free(ns);
+    free(name);
+    if (dynamic) {
+        free(value);
+    }
+    ly_free_val_prefs(lybctx->ctx, val_prefs);
+    if (ret) {
+        ly_free_attr(lybctx->ctx, *attr, 1);
+        *attr = NULL;
+    }
+    return ret;
+}
+
+/**
+ * @brief Check whether a schema node matches a hash(es).
+ *
+ * @param[in] sibling Schema node to check.
+ * @param[in] hash Hash array to check.
+ * @param[in] hash_count Number of hashes in @p hash.
+ * @return non-zero if matches,
+ * @return 0 if not.
+ */
+static int
+lyb_is_schema_hash_match(struct lysc_node *sibling, LYB_HASH *hash, uint8_t hash_count)
+{
+    LYB_HASH sibling_hash;
+    uint8_t i;
+
+    /* compare all the hashes starting from collision ID 0 */
+    for (i = 0; i < hash_count; ++i) {
+        sibling_hash = lyb_hash(sibling, i);
+        if (sibling_hash != hash[i]) {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * @brief Check that a schema node is suitable based on options.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] snode Schema node to check.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_check_schema(struct lyd_lyb_ctx *lybctx, const struct lysc_node *snode)
+{
+    LY_ERR ret = LY_SUCCESS;
+
+    if ((lybctx->options & LYD_OPT_NO_STATE) && (snode->flags & LYS_CONFIG_R)) {
+        LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LY_VCODE_INNODE, "state", snode->name);
+        return LY_EVALID;
+    }
+
+    if (snode->nodetype & (LYS_RPC | LYS_ACTION)) {
+        if (lybctx->int_opts & LYD_INTOPT_RPC) {
+            if (lybctx->op_ntf) {
+                LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+                       lys_nodetype2str(snode->nodetype), snode->name,
+                       lys_nodetype2str(lybctx->op_ntf->schema->nodetype), lybctx->op_ntf->schema->name);
+                return LY_EVALID;
+            }
+        } else {
+            LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
+                   lys_nodetype2str(snode->nodetype), snode->name);
+            return LY_EVALID;
+        }
+    } else if (snode->nodetype == LYS_NOTIF) {
+        if (lybctx->int_opts & LYD_INTOPT_NOTIF) {
+            if (lybctx->op_ntf) {
+                LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\", %s \"%s\" already parsed.",
+                       lys_nodetype2str(snode->nodetype), snode->name,
+                       lys_nodetype2str(lybctx->op_ntf->schema->nodetype), lybctx->op_ntf->schema->name);
+                return LY_EVALID;
+            }
+        } else {
+            LOGVAL(lybctx->ctx, LY_VLOG_LYSC, snode, LYVE_DATA, "Unexpected %s element \"%s\".",
+                   lys_nodetype2str(snode->nodetype), snode->name);
+            return LY_EVALID;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Parse schema node hash.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] sparent Schema parent, must be set if @p mod is not.
+ * @param[in] mod Module of the top-level node, must be set if @p sparent is not.
+ * @param[out] snode Parsed found schema node, may be NULL if opaque.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_schema_hash(struct lyd_lyb_ctx *lybctx, const struct lysc_node *sparent, const struct lys_module *mod,
+                      const struct lysc_node **snode)
+{
+    LY_ERR ret;
+    uint8_t i, j;
+    const struct lysc_node *sibling;
+    LYB_HASH hash[LYB_HASH_BITS - 1];
+    int getnext_opts;
+
+    *snode = NULL;
+    /* leave if-feature check for validation */
+    getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+
+    /* read the first hash */
+    lyb_read(&hash[0], sizeof *hash, lybctx);
+
+    if (!hash[0]) {
+        /* opaque node */
+        return LY_SUCCESS;
+    }
+
+    /* based on the first hash read all the other ones, if any */
+    for (i = 0; !(hash[0] & (LYB_HASH_COLLISION_ID >> i)); ++i) {
+        if (i > LYB_HASH_BITS) {
+            LOGINT_RET(lybctx->ctx);
+        }
+    }
+
+    /* move the first hash on its accurate position */
+    hash[i] = hash[0];
+
+    /* read the rest of hashes */
+    for (j = i; j; --j) {
+        lyb_read(&hash[j - 1], sizeof *hash, lybctx);
+
+        /* correct collision ID */
+        assert(hash[j - 1] & (LYB_HASH_COLLISION_ID >> (j - 1)));
+        /* preceded with zeros */
+        assert(!(hash[j - 1] & (LYB_HASH_MASK << (LYB_HASH_BITS - (j - 1)))));
+    }
+
+    /* find our node with matching hashes */
+    sibling = NULL;
+    while ((sibling = lys_getnext(sibling, sparent, mod ? mod->compiled : NULL, getnext_opts))) {
+        /* skip schema nodes from models not present during printing */
+        if (lyb_has_schema_model(sibling, lybctx->models)
+                && lyb_is_schema_hash_match((struct lysc_node *)sibling, hash, i + 1)) {
+            /* match found */
+            break;
+        }
+    }
+
+    if (!sibling && (lybctx->options & LYD_OPT_STRICT)) {
+        if (mod) {
+            LOGVAL(lybctx->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Failed to find matching hash for a top-level node"
+                   " from \"%s\".", mod->name);
+        } else {
+            LOGVAL(lybctx->ctx, LY_VLOG_LYSC, sparent, LYVE_REFERENCE, "Failed to find matching hash for a child node"
+                   " of \"%s\".", sparent->name);
+        }
+        return LY_EVALID;
+    } else if (sibling && (ret = lyb_parse_check_schema(lybctx, sibling))) {
+        return ret;
+    }
+
+    *snode = sibling;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Read until the end of the current subtree.
+ *
+ * @param[in] lybctx LYB context.
+ */
+static void
+lyb_skip_subtree(struct lyd_lyb_ctx *lybctx)
+{
+    int parsed;
+
+    do {
+        /* first skip any meta information inside */
+        parsed = LYB_LAST_SUBTREE(lybctx).inner_chunks * LYB_META_BYTES;
+        lybctx->data += parsed;
+        lybctx->byte_count += parsed;
+
+        /* then read data */
+        lyb_read(NULL, LYB_LAST_SUBTREE(lybctx).written, lybctx);
+    } while (LYB_LAST_SUBTREE(lybctx).written);
+}
+
+/**
+ * @brief Parse LYB subtree.
+ *
+ * @param[in] lybctx LYB context.
+ * @param[in] parent Data parent of the subtree, must be set if @p first is not.
+ * @param[in,out] first First top-level sibling, must be set if @p parent is not.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_subtree_r(struct lyd_lyb_ctx *lybctx, struct lyd_node_inner *parent, struct lyd_node **first)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_node *node = NULL, *tree;
+    const struct lys_module *mod;
+    const struct lysc_node *snode = NULL;
+    struct lyd_meta *meta = NULL, *m;
+    struct ly_attr *attr = NULL, *a;
+    struct ly_prefix *val_prefs = NULL;
+    LYD_ANYDATA_VALUETYPE value_type;
+    char *value = NULL, *name = NULL, *prefix = NULL, *ns = NULL;
+    int dynamic = 0;
+    LYD_FORMAT format = 0;
+    int prev_lo;
+
+    /* register a new subtree */
+    LY_CHECK_GOTO(ret = lyb_read_start_subtree(lybctx), cleanup);
+
+    if (!parent) {
+        /* top-level, read module name */
+        ret = lyb_parse_model(lybctx, &mod);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* read hash, find the schema node starting from mod */
+        ret = lyb_parse_schema_hash(lybctx, NULL, mod, &snode);
+        LY_CHECK_GOTO(ret, cleanup);
+    } 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, cleanup);
+    }
+
+     if (!snode && !(lybctx->options & LYD_OPT_OPAQ)) {
+        /* unknown data, skip them */
+        lyb_skip_subtree(lybctx);
+        goto stop_subtree;
+     }
+
+    /* create metadata/attributes */
+    if (snode) {
+        ret = lyb_parse_metadata(lybctx, snode, &meta);
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        ret = lyb_parse_attributes(lybctx, &attr);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    if (!snode) {
+        /* parse prefix */
+        ret = lyb_read_string(&prefix, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse namespace */
+        ret = lyb_read_string(&ns, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse name */
+        ret = lyb_read_string(&name, 1, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse value prefixes */
+        ret = lyb_parse_opaq_prefixes(lybctx, &val_prefs);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse format */
+        lyb_read((uint8_t *)&format, 1, lybctx);
+
+        /* parse value */
+        ret = lyb_read_string(&value, 0, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        dynamic = 1;
+
+        /* create node */
+        ret = lyd_create_opaq(lybctx->ctx, name, strlen(name), value, strlen(value), &dynamic, format, val_prefs, prefix,
+                              strlen(prefix), ns, &node);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* process children */
+        while (LYB_LAST_SUBTREE(lybctx).written) {
+            ret = lyb_parse_subtree_r(lybctx, (struct lyd_node_inner *)node, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else if (snode->nodetype & LYD_NODE_TERM) {
+        /* parse value */
+        ret = lyb_read_string(&value, 0, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        dynamic = 1;
+
+        /* create node */
+        ret = lyd_create_term(snode, value, strlen(value), &dynamic, lydjson_resolve_prefix, NULL, LYD_JSON, &node);
+        if (dynamic) {
+            free(value);
+            dynamic = 0;
+        }
+        value = NULL;
+        if (ret == LY_EINCOMPLETE) {
+            if (!(lybctx->options & LYD_OPT_PARSE_ONLY)) {
+                ly_set_add(&lybctx->unres_node_type, node, LY_SET_OPT_USEASLIST);
+            }
+            ret = LY_SUCCESS;
+        } else if (ret) {
+            goto cleanup;
+        }
+    } else if (snode->nodetype & LYD_NODE_INNER) {
+        /* create node */
+        ret = lyd_create_inner(snode, &node);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* process children */
+        while (LYB_LAST_SUBTREE(lybctx).written) {
+            ret = lyb_parse_subtree_r(lybctx, (struct lyd_node_inner *)node, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+
+        if (!(lybctx->options & LYD_OPT_PARSE_ONLY)) {
+            /* new node validation, autodelete CANNOT occur, all nodes are new */
+            ret = lyd_validate_new(lyd_node_children_p(node), snode, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+
+            /* add any missing default children */
+            ret = lyd_validate_defaults_r((struct lyd_node_inner *)node, lyd_node_children_p(node), NULL, NULL,
+                                          &lybctx->unres_node_type, &lybctx->when_check, lybctx->options);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+
+        if (snode->nodetype == LYS_LIST) {
+            /* hash now that all keys should be parsed, rehash for key-less list */
+            lyd_hash(node);
+        } else if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+            /* rememeber the RPC/action/notification */
+            lybctx->op_ntf = node;
+        }
+    } else if (snode->nodetype & LYD_NODE_ANY) {
+        /* parse value type */
+        lyb_read((uint8_t *)&value_type, sizeof value_type, lybctx);
+        if (value_type == LYD_ANYDATA_DATATREE) {
+            /* invalid situation */
+            LOGINT(lybctx->ctx);
+            goto cleanup;
+        }
+
+        /* read anydata content */
+        ret = lyb_read_string(&value, 0, lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+        dynamic = 1;
+
+        if (value_type == LYD_ANYDATA_LYB) {
+            /* turn logging off */
+            prev_lo = ly_log_options(0);
+
+            /* try to parse LYB into a data tree */
+            tree = lyd_parse_mem((struct ly_ctx *)lybctx->ctx, value, LYD_LYB,
+                                 LYD_OPT_PARSE_ONLY | LYD_OPT_OPAQ | LYD_OPT_STRICT);
+            ly_log_options(prev_lo);
+            if (!ly_errcode(lybctx->ctx)) {
+                /* successfully parsed */
+                free(value);
+                value = (char *)tree;
+                value_type = LYD_ANYDATA_DATATREE;
+            }
+        }
+
+        /* create node */
+        ret = lyd_create_any(snode, value, value_type, &node);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        dynamic = 0;
+        value = NULL;
+    }
+    assert(node);
+
+    /* add/correct flags */
+    if (snode) {
+        lyd_parse_set_data_flags(node, &lybctx->when_check, &meta, lybctx->options);
+    }
+
+    /* add metadata/attributes */
+    if (snode) {
+        LY_LIST_FOR(meta, m) {
+            m->parent = node;
+        }
+        node->meta = meta;
+        meta = NULL;
+    } else {
+        assert(!node->schema);
+        LY_LIST_FOR(attr, a) {
+            a->parent = (struct lyd_node_opaq *)node;
+        }
+        ((struct lyd_node_opaq *)node)->attr = attr;
+        attr = NULL;
+    }
+
+    /* insert */
+    lyd_insert_node((struct lyd_node *)parent, first, node);
+    node = NULL;
+
+stop_subtree:
+    /* end the subtree */
+    ret = lyb_read_stop_subtree(lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+    free(prefix);
+    free(ns);
+    free(name);
+    if (dynamic) {
+        free(value);
+    }
+    ly_free_val_prefs(lybctx->ctx, val_prefs);
+
+    lyd_free_meta(lybctx->ctx, meta, 1);
+    ly_free_attr(lybctx->ctx, attr, 1);
+    lyd_free_tree(node);
+    return ret;
+}
+
+/**
+ * @brief Parse used YANG data models.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_data_models(struct lyd_lyb_ctx *lybctx)
+{
+    LY_ERR ret;
+    uint32_t count;
+    LY_ARRAY_SIZE_TYPE u;
+
+    /* read model count */
+    lyb_read_number(&count, sizeof count, 2, lybctx);
+
+    if (count) {
+        LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->models, count, LY_EMEM);
+
+        /* read modules */
+        for (u = 0; u < count; ++u) {
+            ret = lyb_parse_model(lybctx, &lybctx->models[u]);
+            LY_CHECK_RET(ret);
+            LY_ARRAY_INCREMENT(lybctx->models);
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse LYB magic number.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_magic_number(struct lyd_lyb_ctx *lybctx)
+{
+    char magic_byte = 0;
+
+    lyb_read((uint8_t *)&magic_byte, 1, lybctx);
+    if (magic_byte != 'l') {
+        LOGERR(lybctx->ctx, LY_EINVAL, "Invalid first magic number byte \"0x%02x\".", magic_byte);
+        return LY_EINVAL;
+    }
+
+    lyb_read((uint8_t *)&magic_byte, 1, lybctx);
+    if (magic_byte != 'y') {
+        LOGERR(lybctx->ctx, LY_EINVAL, "Invalid second magic number byte \"0x%02x\".", magic_byte);
+        return LY_EINVAL;
+    }
+
+    lyb_read((uint8_t *)&magic_byte, 1, lybctx);
+    if (magic_byte != 'b') {
+        LOGERR(lybctx->ctx, LY_EINVAL, "Invalid third magic number byte \"0x%02x\".", magic_byte);
+        return LY_EINVAL;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Parse LYB header.
+ *
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_parse_header(struct lyd_lyb_ctx *lybctx)
+{
+    uint8_t byte = 0;
+
+    /* version, future flags */
+    lyb_read((uint8_t *)&byte, sizeof byte, lybctx);
+
+    if ((byte & LYB_VERSION_MASK) != LYB_VERSION_NUM) {
+        LOGERR(lybctx->ctx, LY_EINVAL, "Invalid LYB format version \"0x%02x\", expected \"0x%02x\".",
+               byte & LYB_VERSION_MASK, LYB_VERSION_NUM);
+        return LY_EINVAL;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyd_parse_lyb_data(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **tree, int *parsed_bytes)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_lyb_ctx lybctx = {0};
+
+    *tree = NULL;
+
+    lybctx.data = data;
+    lybctx.ctx = ctx;
+    lybctx.options = options;
+
+    /* read magic number */
+    ret = lyb_parse_magic_number(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read header */
+    ret = lyb_parse_header(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read used models */
+    ret = lyb_parse_data_models(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read subtree(s) */
+    while (lybctx.data[0]) {
+        ret = lyb_parse_subtree_r(&lybctx, NULL, tree);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* read the last zero, parsing finished */
+    ++lybctx.byte_count;
+    ++lybctx.data;
+
+    /* TODO validation */
+
+cleanup:
+    LY_ARRAY_FREE(lybctx.subtrees);
+    LY_ARRAY_FREE(lybctx.models);
+    ly_set_erase(&lybctx.unres_node_type, NULL);
+    ly_set_erase(&lybctx.unres_meta_type, NULL);
+    ly_set_erase(&lybctx.when_check, NULL);
+
+    if (parsed_bytes) {
+        *parsed_bytes = lybctx.byte_count;
+    }
+    if (ret) {
+        lyd_free_all(*tree);
+        *tree = NULL;
+    }
+    return ret;
+}
+
+LY_ERR
+lyd_parse_lyb_rpc(struct ly_ctx *ctx, const char *data, struct lyd_node **tree, struct lyd_node **op, int *parsed_bytes)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_lyb_ctx lybctx = {0};
+
+    lybctx.data = data;
+    lybctx.ctx = ctx;
+    lybctx.options = LYD_OPT_PARSE_ONLY | LYD_OPT_STRICT;
+    lybctx.int_opts = LYD_INTOPT_RPC;
+
+    *tree = NULL;
+    if (op) {
+        *op = NULL;
+    }
+
+    /* read magic number */
+    ret = lyb_parse_magic_number(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read header */
+    ret = lyb_parse_header(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read used models */
+    ret = lyb_parse_data_models(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read subtree(s) */
+    while (lybctx.data[0]) {
+        ret = lyb_parse_subtree_r(&lybctx, NULL, tree);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* read the last zero, parsing finished */
+    ++lybctx.byte_count;
+    ++lybctx.data;
+
+    /* make sure we have parsed some operation */
+    if (!lybctx.op_ntf) {
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"rpc\"/\"action\" node.");
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+    if (op) {
+        *op = lybctx.op_ntf;
+    }
+    assert(*tree);
+
+cleanup:
+    LY_ARRAY_FREE(lybctx.subtrees);
+    LY_ARRAY_FREE(lybctx.models);
+    assert(!lybctx.unres_node_type.count && !lybctx.unres_meta_type.count && !lybctx.when_check.count);
+
+    if (parsed_bytes) {
+        *parsed_bytes = lybctx.byte_count;
+    }
+    if (ret) {
+        lyd_free_all(*tree);
+        *tree = NULL;
+    }
+    return ret;
+}
+
+LY_ERR
+lyd_parse_lyb_notif(struct ly_ctx *ctx, const char *data, struct lyd_node **tree, struct lyd_node **ntf, int *parsed_bytes)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_lyb_ctx lybctx = {0};
+
+    lybctx.data = data;
+    lybctx.ctx = ctx;
+    lybctx.options = LYD_OPT_PARSE_ONLY | LYD_OPT_STRICT;
+    lybctx.int_opts = LYD_INTOPT_NOTIF;
+
+    *tree = NULL;
+    if (ntf) {
+        *ntf = NULL;
+    }
+
+    /* read magic number */
+    ret = lyb_parse_magic_number(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read header */
+    ret = lyb_parse_header(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read used models */
+    ret = lyb_parse_data_models(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read subtree(s) */
+    while (lybctx.data[0]) {
+        ret = lyb_parse_subtree_r(&lybctx, NULL, tree);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* read the last zero, parsing finished */
+    ++lybctx.byte_count;
+    ++lybctx.data;
+
+    /* make sure we have parsed some notification */
+    if (!lybctx.op_ntf) {
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_DATA, "Missing the \"notification\" node.");
+        ret = LY_EVALID;
+        goto cleanup;
+    }
+
+    if (ntf) {
+        *ntf = lybctx.op_ntf;
+    }
+    assert(*tree);
+
+cleanup:
+    LY_ARRAY_FREE(lybctx.subtrees);
+    LY_ARRAY_FREE(lybctx.models);
+    assert(!lybctx.unres_node_type.count && !lybctx.unres_meta_type.count && !lybctx.when_check.count);
+
+    if (parsed_bytes) {
+        *parsed_bytes = lybctx.byte_count;
+    }
+    if (ret) {
+        lyd_free_all(*tree);
+        *tree = NULL;
+    }
+    return ret;
+}
+
+LY_ERR
+lyd_parse_lyb_reply(struct lyd_node *request, const char *data, struct lyd_node **tree, struct lyd_node **op,
+                    int *parsed_bytes)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_lyb_ctx lybctx = {0};
+    struct lyd_node *iter, *req_op, *rep_op = NULL;
+
+    lybctx.data = data;
+    lybctx.ctx = LYD_NODE_CTX(request);
+    lybctx.options = LYD_OPT_PARSE_ONLY | LYD_OPT_STRICT;
+    lybctx.int_opts = LYD_INTOPT_REPLY;
+
+    *tree = NULL;
+    if (op) {
+        *op = NULL;
+    }
+
+    /* find request OP */
+    LYD_TREE_DFS_BEGIN((struct lyd_node *)request, iter, req_op) {
+        if (req_op->schema->nodetype & (LYS_RPC | LYS_ACTION)) {
+            break;
+        }
+        LYD_TREE_DFS_END(request, iter, req_op);
+    }
+    if (!(req_op->schema->nodetype & (LYS_RPC | LYS_ACTION))) {
+        LOGERR(LYD_NODE_CTX(request), LY_EINVAL, "No RPC/action in the request found.");
+        ret = LY_EINVAL;
+        goto cleanup;
+    }
+
+    /* duplicate request OP with parents */
+    rep_op = lyd_dup(req_op, NULL, LYD_DUP_WITH_PARENTS);
+    LY_CHECK_ERR_GOTO(!rep_op, ret = LY_EMEM, cleanup);
+
+    /* read magic number */
+    ret = lyb_parse_magic_number(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read header */
+    ret = lyb_parse_header(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read used models */
+    ret = lyb_parse_data_models(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read subtree(s) */
+    while (lybctx.data[0]) {
+        ret = lyb_parse_subtree_r(&lybctx, (struct lyd_node_inner *)rep_op, NULL);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* read the last zero, parsing finished */
+    ++lybctx.byte_count;
+    ++lybctx.data;
+
+    if (op) {
+        *op = rep_op;
+    }
+    for (iter = rep_op; iter->parent; iter = (struct lyd_node *)iter->parent);
+    *tree = iter;
+    rep_op = NULL;
+
+cleanup:
+    lyd_free_all(rep_op);
+    LY_ARRAY_FREE(lybctx.subtrees);
+    LY_ARRAY_FREE(lybctx.models);
+    assert(!lybctx.unres_node_type.count && !lybctx.unres_meta_type.count && !lybctx.when_check.count);
+
+    if (parsed_bytes) {
+        *parsed_bytes = lybctx.byte_count;
+    }
+    if (ret) {
+        lyd_free_all(*tree);
+        *tree = NULL;
+    }
+    return ret;
+}
+
+API int
+lyd_lyb_data_length(const char *data)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_lyb_ctx lybctx = {0};
+    int count, i;
+    size_t len;
+    uint8_t buf[LYB_SIZE_MAX];
+
+    if (!data) {
+        return -1;
+    }
+
+    lybctx.data = data;
+
+    /* read magic number */
+    ret = lyb_parse_magic_number(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read header */
+    ret = lyb_parse_header(&lybctx);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* read model count */
+    lyb_read_number(&count, sizeof count, 2, &lybctx);
+
+    /* read all models */
+    for (i = 0; i < count; ++i) {
+        /* module name length */
+        len = 0;
+        lyb_read_number(&len, sizeof len, 2, &lybctx);
+
+        /* model name */
+        lyb_read(buf, len, &lybctx);
+
+        /* revision */
+        lyb_read(buf, 2, &lybctx);
+    }
+
+    while (lybctx.data[0]) {
+        /* register a new subtree */
+        ret = lyb_read_start_subtree(&lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* skip it */
+        lyb_skip_subtree(&lybctx);
+
+        /* subtree finished */
+        ret = lyb_read_stop_subtree(&lybctx);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* read the last zero, parsing finished */
+    ++lybctx.byte_count;
+    ++lybctx.data;
+
+cleanup:
+    LY_ARRAY_FREE(lybctx.subtrees);
+    return ret ? -1 : (signed)lybctx.byte_count;
+}
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 0ebd959..a50ee05 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -27,10 +27,6 @@
 #include "validation.h"
 #include "xml.h"
 
-#define LYD_INTOPT_RPC      0x01    /**< RPC/action invocation is being parsed */
-#define LYD_INTOPT_NOTIF    0x02    /**< notification is being parsed */
-#define LYD_INTOPT_REPLY    0x04    /**< RPC/action reply is being parsed */
-
 /**
  * @brief Internal context for XML YANG data parser.
  */
@@ -67,7 +63,7 @@
 }
 
 static LY_ERR
-lydxml_metadata(struct lyxml_ctx *xmlctx, const struct lysc_node *sparent, int strict, struct ly_set *type_meta_check,
+lydxml_metadata(struct lyxml_ctx *xmlctx, const struct lysc_node *sparent, int strict, struct ly_set *unres_meta_type,
                 struct lyd_meta **meta)
 {
     LY_ERR ret = LY_EVALID;
@@ -116,7 +112,7 @@
             goto skip_attr;
         }
 
-        /* remember attr name and get its content */
+        /* remember meta name and get its content */
         name = xmlctx->name;
         name_len = xmlctx->name_len;
         LY_CHECK_GOTO(ret = lyxml_ctx_next(xmlctx), cleanup);
@@ -126,8 +122,8 @@
         ret = lyd_create_meta(NULL, meta, mod, name, name_len, xmlctx->value, xmlctx->value_len, &xmlctx->dynamic,
                               lydxml_resolve_prefix, xmlctx, LYD_XML, sparent);
         if (ret == LY_EINCOMPLETE) {
-            if (type_meta_check) {
-                ly_set_add(type_meta_check, meta, LY_SET_OPT_USEASLIST);
+            if (unres_meta_type) {
+                ly_set_add(unres_meta_type, *meta, LY_SET_OPT_USEASLIST);
             }
         } else if (ret) {
             goto cleanup;
@@ -400,46 +396,6 @@
     return ret;
 }
 
-static void
-lydxml_data_flags(struct lyd_xml_ctx *lydctx, struct lyd_node *node, struct lyd_meta **meta)
-{
-    struct lyd_meta *meta2, *prev_meta = NULL;
-
-    if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && node->schema->when) {
-        if (lydctx->options & LYD_OPT_TRUSTED) {
-            /* just set it to true */
-            node->flags |= LYD_WHEN_TRUE;
-        } else {
-            /* remember we need to evaluate this node's when */
-            ly_set_add(&lydctx->when_check, node, LY_SET_OPT_USEASLIST);
-        }
-    }
-
-    if (lydctx->options & LYD_OPT_TRUSTED) {
-        /* node is valid */
-        node->flags &= ~LYD_NEW;
-    }
-
-    LY_LIST_FOR(*meta, meta2) {
-        if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults")
-                && meta2->value.boolean) {
-            /* node is default according to the metadata */
-            node->flags |= LYD_DEFAULT;
-
-            /* delete the metadata */
-            if (prev_meta) {
-                prev_meta->next = meta2->next;
-            } else {
-                *meta = (*meta)->next;
-            }
-            lyd_free_meta(lydctx->xmlctx->ctx, meta2, 0);
-            break;
-        }
-
-        prev_meta = meta2;
-    }
-}
-
 /**
  * @brief Parse XML elements as YANG data node children the specified parent node.
  *
@@ -675,7 +631,7 @@
 
         /* add/correct flags */
         if (snode) {
-            lydxml_data_flags(lydctx, cur, &meta);
+            lyd_parse_set_data_flags(cur, &lydctx->when_check, &meta, lydctx->options);
         }
 
         /* add metadata/attributes */
diff --git a/src/path.c b/src/path.c
index e72485b..47ef657 100644
--- a/src/path.c
+++ b/src/path.c
@@ -363,6 +363,7 @@
             *mod = prev_ctx_node->module;
             break;
         case LYD_XML:
+        case LYD_LYB:
             /* not really defined */
             LOGINT_RET(ctx);
         }
diff --git a/src/printer_data.c b/src/printer_data.c
index a554e01..73e8d54 100644
--- a/src/printer_data.c
+++ b/src/printer_data.c
@@ -41,12 +41,12 @@
     case LYD_JSON:
         ret = json_print_data(out, root, options);
         break;
+#endif
     case LYD_LYB:
         ret = lyb_print_data(out, root, options);
         break;
-#endif
-    default:
-        LOGERR(out->ctx, LY_EINVAL, "Unknown output format.");
+    case LYD_SCHEMA:
+        LOGERR(out->ctx, LY_EINVAL, "Invalid output format.");
         ret = LY_EINVAL;
         break;
     }
diff --git a/src/printer_internal.h b/src/printer_internal.h
index 9c926fb..d71d1a5 100644
--- a/src/printer_internal.h
+++ b/src/printer_internal.h
@@ -123,7 +123,7 @@
 LY_ERR yin_print_parsed(struct ly_out *out, const struct lys_module *module);
 
 /**
- * @brief XML printer of the YANG data.
+ * @brief XML printer of YANG data.
  *
  * @param[in] out Output specification.
  * @param[in] root The root element of the (sub)tree to print.
@@ -133,6 +133,16 @@
 LY_ERR xml_print_data(struct ly_out *out, const struct lyd_node *root, int options);
 
 /**
+ * @brief LYB printer of YANG data.
+ *
+ * @param[in] out Output structure.
+ * @param[in] root The root element of the (sub)tree to print.
+ * @param[in] options [Data printer flags](@ref dataprinterflags).
+ * @return LY_ERR value, number of the printed bytes is updated in lyout::printed.
+ */
+LY_ERR lyb_print_data(struct ly_out *out, const struct lyd_node *root, int options);
+
+/**
  * @brief Check whether a node value equals to its default one.
  *
  * @param[in] node Term node to test.
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
new file mode 100644
index 0000000..0d95bbd
--- /dev/null
+++ b/src/printer_lyb.c
@@ -0,0 +1,1045 @@
+/**
+ * @file printer_lyb.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief LYB printer for libyang data structure
+ *
+ * Copyright (c) 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#include "lyb.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "log.h"
+#include "printer.h"
+#include "printer_internal.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+
+/**
+ * @brief Hash table equal callback for checking hash equality only.
+ */
+static int
+lyb_hash_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 does also, we do not want 2 values to have the same hash */
+    return 1;
+}
+
+/**
+ * @brief Hash table equal callback for checking value pointer equality only.
+ */
+static int
+lyb_ptr_equal_cb(void *val1_p, void *val2_p, int UNUSED(mod), void *UNUSED(cb_data))
+{
+    struct lysc_node *val1 = *(struct lysc_node **)val1_p;
+    struct lysc_node *val2 = *(struct lysc_node **)val2_p;
+
+    if (val1 == val2) {
+        return 1;
+    }
+    return 0;
+}
+
+/**
+ * @brief Check that sibling collision hash is safe to insert into hash table.
+ *
+ * @param[in] ht Hash table.
+ * @param[in] sibling Hashed sibling.
+ * @param[in] ht_col_id Sibling hash collision ID.
+ * @param[in] compare_col_id Last collision ID to compare with.
+ * @return LY_SUCCESS when the whole hash sequence does not collide,
+ * @return LY_EEXIST when the whole hash sequence sollides.
+ */
+static LY_ERR
+lyb_hash_sequence_check(struct hash_table *ht, struct lysc_node *sibling, int ht_col_id, int compare_col_id)
+{
+    int j;
+    struct lysc_node **col_node;
+
+    /* get the first node inserted with last hash col ID ht_col_id */
+    if (lyht_find(ht, &sibling, lyb_hash(sibling, ht_col_id), (void **)&col_node)) {
+        /* there is none. valid situation */
+        return LY_SUCCESS;
+    }
+
+    lyht_set_cb(ht, lyb_ptr_equal_cb);
+    do {
+        for (j = compare_col_id; j > -1; --j) {
+            if (lyb_hash(sibling, j) != lyb_hash(*col_node, j)) {
+                /* one non-colliding hash */
+                break;
+            }
+        }
+        if (j == -1) {
+            /* all whole hash sequences of nodes inserted with last hash col ID compare_col_id collide */
+            lyht_set_cb(ht, lyb_hash_equal_cb);
+            return LY_EEXIST;
+        }
+
+        /* get next node inserted with last hash col ID ht_col_id */
+    } while (!lyht_find_next(ht, col_node, lyb_hash(*col_node, ht_col_id), (void **)&col_node));
+
+    lyht_set_cb(ht, lyb_hash_equal_cb);
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Hash all the siblings and add them also into a separate hash table.
+ *
+ * @param[in] sibling Any sibling in all the siblings on one level.
+ * @param[out] ht_p Created hash table.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_hash_siblings(struct lysc_node *sibling, struct hash_table **ht_p)
+{
+    struct hash_table *ht;
+    const struct lysc_node *parent;
+    const struct lys_module *mod;
+    int i, j;
+
+    ht = lyht_new(1, sizeof(struct lysc_node *), lyb_hash_equal_cb, NULL, 1);
+    LY_CHECK_ERR_RET(!ht, LOGMEM(sibling->module->ctx), LY_EMEM);
+
+    parent = lysc_data_parent(sibling);
+    mod = sibling->module;
+
+    sibling = NULL;
+    /* ignore features so that their state does not affect hashes */
+    while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, LYS_GETNEXT_NOSTATECHECK))) {
+        /* find the first non-colliding hash (or specifically non-colliding hash sequence) */
+        for (i = 0; i < LYB_HASH_BITS; ++i) {
+            /* check that we are not colliding with nodes inserted with a lower collision ID than ours */
+            for (j = i - 1; j > -1; --j) {
+                if (lyb_hash_sequence_check(ht, sibling, j, i)) {
+                    break;
+                }
+            }
+            if (j > -1) {
+                /* some check failed, we must use a higher collision ID */
+                continue;
+            }
+
+            /* try to insert node with the current collision ID */
+            if (!lyht_insert_with_resize_cb(ht, &sibling, lyb_hash(sibling, i), lyb_ptr_equal_cb, NULL)) {
+                /* success, no collision */
+                break;
+            }
+
+            /* make sure we really cannot insert it with this hash col ID (meaning the whole hash sequence is colliding) */
+            if (i && !lyb_hash_sequence_check(ht, sibling, i, i)) {
+                /* it can be inserted after all, even though there is already a node with the same last collision ID */
+                lyht_set_cb(ht, lyb_ptr_equal_cb);
+                if (lyht_insert(ht, &sibling, lyb_hash(sibling, i), NULL)) {
+                    LOGINT(sibling->module->ctx);
+                    lyht_set_cb(ht, lyb_hash_equal_cb);
+                    lyht_free(ht);
+                    return LY_EINT;
+                }
+                lyht_set_cb(ht, lyb_hash_equal_cb);
+                break;
+            }
+            /* there is still another colliding schema node with the same hash sequence, try higher collision ID */
+        }
+
+        if (i == LYB_HASH_BITS) {
+            /* wow */
+            LOGINT(sibling->module->ctx);
+            lyht_free(ht);
+            return LY_EINT;
+        }
+    }
+
+    /* change val equal callback so that the HT is usable for finding value hashes */
+    lyht_set_cb(ht, lyb_ptr_equal_cb);
+
+    *ht_p = ht;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Find node hash in a hash table.
+ *
+ * @param[in] ht Hash table to search in.
+ * @param[in] node Node to find.
+ * @param[out] hash_p First non-colliding hash found.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_hash_find(struct hash_table *ht, struct lysc_node *node, LYB_HASH *hash_p)
+{
+    LYB_HASH hash;
+    uint32_t i;
+
+    for (i = 0; i < LYB_HASH_BITS; ++i) {
+        hash = lyb_hash(node, i);
+        if (!hash) {
+            LOGINT_RET(node->module->ctx);
+        }
+
+        if (!lyht_find(ht, &node, hash, NULL)) {
+            /* success, no collision */
+            break;
+        }
+    }
+    /* cannot happen, we already calculated the hash */
+    if (i == LYB_HASH_BITS) {
+        LOGINT_RET(node->module->ctx);
+    }
+
+    *hash_p = hash;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Write LYB data fully handling the metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] buf Source buffer.
+ * @param[in] count Number of bytes to write.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write(struct ly_out *out, const uint8_t *buf, size_t count, struct lyd_lyb_ctx *lybctx)
+{
+    LY_ARRAY_SIZE_TYPE u;
+    struct lyd_lyb_subtree *full, *iter;
+    ssize_t r, to_write;
+    uint8_t meta_buf[LYB_META_BYTES];
+
+    while (1) {
+        /* check for full data chunks */
+        to_write = count;
+        full = NULL;
+        LY_ARRAY_FOR(lybctx->subtrees, u) {
+            /* we want the innermost chunks resolved first, so replace previous full chunks */
+            if (lybctx->subtrees[u].written + to_write >= LYB_SIZE_MAX) {
+                /* full chunk, do not write more than allowed */
+                to_write = LYB_SIZE_MAX - lybctx->subtrees[u].written;
+                full = &lybctx->subtrees[u];
+            }
+        }
+
+        if (!full && !count) {
+            break;
+        }
+
+        /* we are actually writing some data, not just finishing another chunk */
+        if (to_write) {
+            r = ly_write(out, (char *)buf, to_write);
+            if (r < to_write) {
+                return LY_ESYS;
+            }
+            lybctx->byte_count += r;
+
+            LY_ARRAY_FOR(lybctx->subtrees, u) {
+                /* increase all written counters */
+                lybctx->subtrees[u].written += r;
+                assert(lybctx->subtrees[u].written <= LYB_SIZE_MAX);
+            }
+            /* decrease count/buf */
+            count -= r;
+            buf += r;
+        }
+
+        if (full) {
+            /* write the meta information (inner chunk count and chunk size) */
+            meta_buf[0] = full->written & 0xFF;
+            meta_buf[1] = full->inner_chunks & 0xFF;
+
+            r = ly_write_skipped(out, full->position, (char *)meta_buf, LYB_META_BYTES);
+            if (r < 0) {
+                return LY_ESYS;
+            }
+            /* these bytes were already counted */
+
+            /* zero written and inner chunks */
+            full->written = 0;
+            full->inner_chunks = 0;
+
+            /* skip space for another chunk size */
+            r = ly_write_skip(out, LYB_META_BYTES, &full->position);
+            if (r < LYB_META_BYTES) {
+                return LY_ESYS;
+            }
+            lybctx->byte_count += r;
+
+            /* increase inner chunk count */
+            for (iter = &lybctx->subtrees[0]; iter != full; ++iter) {
+                if (iter->inner_chunks == LYB_INCHUNK_MAX) {
+                    LOGINT(lybctx->ctx);
+                    return LY_EINT;
+                }
+                ++iter->inner_chunks;
+            }
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Stop the current subtree - write its final metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_stop_subtree(struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    ssize_t r;
+    uint8_t meta_buf[LYB_META_BYTES];
+
+    /* write the meta chunk information */
+    meta_buf[0] = LYB_LAST_SUBTREE(lybctx).written & 0xFF;
+    meta_buf[1] = LYB_LAST_SUBTREE(lybctx).inner_chunks & 0xFF;
+
+    r = ly_write_skipped(out, LYB_LAST_SUBTREE(lybctx).position, (char *)&meta_buf, LYB_META_BYTES);
+    if (r < 0) {
+        return LY_ESYS;
+    }
+    /* do not count these bytes */
+
+    LY_ARRAY_DECREMENT(lybctx->subtrees);
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Start a new subtree - skip bytes for its metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_start_subtree(struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    ssize_t r;
+    LY_ARRAY_SIZE_TYPE u;
+
+    if (!lybctx->subtrees) {
+        u = 0;
+    } else {
+        u = LY_ARRAY_SIZE(lybctx->subtrees);
+    }
+    if (u == lybctx->subtree_size) {
+        LY_ARRAY_CREATE_RET(lybctx->ctx, lybctx->subtrees, u + LYB_SUBTREE_STEP, LY_EMEM);
+        lybctx->subtree_size = u + LYB_SUBTREE_STEP;
+    }
+
+    LY_ARRAY_INCREMENT(lybctx->subtrees);
+    LYB_LAST_SUBTREE(lybctx).written = 0;
+    LYB_LAST_SUBTREE(lybctx).inner_chunks = 0;
+
+    /* another inner chunk */
+    for (u = 0; u < LY_ARRAY_SIZE(lybctx->subtrees) - 1; ++u) {
+        if (lybctx->subtrees[u].inner_chunks == LYB_INCHUNK_MAX) {
+            LOGINT(lybctx->ctx);
+            return -1;
+        }
+        ++lybctx->subtrees[u].inner_chunks;
+    }
+
+    r = ly_write_skip(out, LYB_META_BYTES, &LYB_LAST_SUBTREE(lybctx).position);
+    if (r < LYB_META_BYTES) {
+        return LY_ESYS;
+    }
+    lybctx->byte_count += r;
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Write a number.
+ *
+ * @param[in] num Number to write.
+ * @param[in] bytes Actual accessible bytes of @p num.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_number(uint64_t num, size_t bytes, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    /* correct byte order */
+    num = htole64(num);
+
+    return lyb_write(out, (uint8_t *)&num, bytes, lybctx);
+}
+
+/**
+ * @brief Write a string.
+ *
+ * @param[in] str String to write.
+ * @param[in] str_len Length of @p str.
+ * @param[in] with_length Whether to precede the string with its length.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_write_string(const char *str, size_t str_len, int with_length, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    int r;
+
+    if (!str) {
+        str = "";
+    }
+    if (!str_len) {
+        str_len = strlen(str);
+    }
+
+    if (with_length) {
+        /* print length on 2 bytes */
+        if (str_len > UINT16_MAX) {
+            LOGINT(lybctx->ctx);
+            return LY_EINT;
+        }
+        LY_CHECK_RET(lyb_write_number(str_len, 2, out, lybctx));
+    }
+
+    r = lyb_write(out, (const uint8_t *)str, str_len, lybctx);
+    if (r < 0) {
+        return LY_ESYS;
+    }
+    lybctx->byte_count += r;
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print YANG module info.
+ *
+ * @param[in] out Out structure.
+ * @param[in] mod Module to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_model(struct ly_out *out, const struct lys_module *mod, struct lyd_lyb_ctx *lybctx)
+{
+    int r;
+    uint16_t revision;
+
+    /* model name length and model name */
+    if (mod) {
+        LY_CHECK_RET(lyb_write_string(mod->name, 0, 1, out, lybctx));
+    } else {
+        LY_CHECK_RET(lyb_write_string("", 0, 1, 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) {
+        r = atoi(mod->revision);
+        r -= 2000;
+        r <<= 9;
+
+        revision |= r;
+
+        r = atoi(mod->revision + 5);
+        r <<= 5;
+
+        revision |= r;
+
+        r = atoi(mod->revision + 8);
+
+        revision |= r;
+    }
+    LY_CHECK_RET(lyb_write_number(revision, sizeof revision, out, lybctx));
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print all used YANG modules.
+ *
+ * @param[in] out Out structure.
+ * @param[in] root Data root.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_data_models(struct ly_out *out, const struct lyd_node *root, struct lyd_lyb_ctx *lybctx)
+{
+    struct ly_set *set;
+    LY_ARRAY_SIZE_TYPE u;
+    LY_ERR ret = LY_SUCCESS;
+    struct lys_module *mod;
+    const struct lyd_node *node;
+    uint32_t i;
+
+    set = ly_set_new();
+    LY_CHECK_RET(!set, LY_EMEM);
+
+    /* collect all data node modules */
+    LY_LIST_FOR(root, node) {
+        if (!node->schema) {
+            continue;
+        }
+
+        mod = node->schema->module;
+        ly_set_add(set, mod, 0);
+
+        /* add also their modules deviating or augmenting them */
+        LY_ARRAY_FOR(mod->compiled->deviated_by, u) {
+            ly_set_add(set, mod->compiled->deviated_by[u], 0);
+        }
+        LY_ARRAY_FOR(mod->compiled->augmented_by, u) {
+            ly_set_add(set, mod->compiled->augmented_by[u], 0);
+        }
+    }
+
+    /* now write module count on 2 bytes */
+    LY_CHECK_GOTO(ret = lyb_write_number(set->count, 2, out, lybctx), cleanup);
+
+    /* and all the used models */
+    for (i = 0; i < set->count; ++i) {
+        LY_CHECK_GOTO(ret = lyb_print_model(out, set->objs[i], lybctx), cleanup);
+    }
+
+cleanup:
+    ly_set_free(set, NULL);
+    return ret;
+}
+
+/**
+ * @brief Print LYB magic number.
+ *
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_magic_number(struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    int r;
+    uint32_t magic_number;
+
+    /* 'l', 'y', 'b' - 0x6c7962 */
+    ((char *)&magic_number)[0] = 'l';
+    ((char *)&magic_number)[1] = 'y';
+    ((char *)&magic_number)[2] = 'b';
+
+    r = ly_write(out, (char *)&magic_number, 3);
+    if (r < 3) {
+        return LY_ESYS;
+    }
+    lybctx->byte_count += 3;
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print LYB header.
+ *
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_header(struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    int r;
+    uint8_t byte = 0;
+
+    /* version, future flags */
+    byte |= LYB_VERSION_NUM;
+
+    r = ly_write(out, (char *)&byte, 1);
+    if (r < 1) {
+        return LY_ESYS;
+    }
+    lybctx->byte_count += 1;
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print opaque prefixes.
+ *
+ * @param[in] out Out structure.
+ * @param[in] prefs Prefixes to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_opaq_prefixes(struct ly_out *out, const struct ly_prefix *prefs, struct lyd_lyb_ctx *lybctx)
+{
+    uint8_t count;
+    LY_ARRAY_SIZE_TYPE u;
+
+    if (prefs && (LY_ARRAY_SIZE(prefs) > UINT8_MAX)) {
+        LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of prefixes is %u.", UINT8_MAX);
+        return LY_EINT;
+    }
+
+    count = prefs ? LY_ARRAY_SIZE(prefs) : 0;
+
+    /* write number of prefixes on 1 byte */
+    LY_CHECK_RET(lyb_write(out, &count, 1, lybctx));
+
+    /* write all the prefixes */
+    LY_ARRAY_FOR(prefs, u) {
+        /* prefix */
+        LY_CHECK_RET(lyb_write_string(prefs[u].pref, 0, 1, out, lybctx));
+
+        /* namespace */
+        LY_CHECK_RET(lyb_write_string(prefs[u].ns, 0, 1, out, lybctx));
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print opaque node.
+ *
+ * @param[in] opaq Node to print.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_opaq(struct lyd_node_opaq *opaq, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    /* prefix */
+    LY_CHECK_RET(lyb_write_string(opaq->prefix.pref, 0, 1, out, lybctx));
+
+    /* namespace */
+    LY_CHECK_RET(lyb_write_string(opaq->prefix.ns, 0, 1, out, lybctx));
+
+    /* name */
+    LY_CHECK_RET(lyb_write_string(opaq->name, 0, 1, out, lybctx));
+
+    /* value prefixes */
+    LY_CHECK_RET(lyb_print_opaq_prefixes(out, opaq->val_prefs, lybctx));
+
+    /* format */
+    LY_CHECK_RET(lyb_write_number(opaq->format, 1, out, lybctx));
+
+    /* value */
+    LY_CHECK_RET(lyb_write_string(opaq->value, 0, 0, out, lybctx));
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print anydata node.
+ *
+ * @param[in] anydata Node to print.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_anydata(struct lyd_node_any *anydata, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    LY_ERR ret = LY_SUCCESS;
+    LYD_ANYDATA_VALUETYPE value_type;
+    int len;
+    char *buf = NULL;
+    const char *str;
+    struct ly_out *out2 = NULL;
+
+    if (anydata->value_type == LYD_ANYDATA_DATATREE) {
+        /* will be printed as a nested LYB data tree */
+        value_type = LYD_ANYDATA_LYB;
+    } else {
+        value_type = anydata->value_type;
+    }
+
+    /* first byte is type */
+    LY_CHECK_GOTO(ret = lyb_write(out, (uint8_t *)&value_type, sizeof value_type, lybctx), cleanup);
+
+    if (anydata->value_type == LYD_ANYDATA_DATATREE) {
+        /* print LYB data tree to memory */
+        LY_CHECK_GOTO(ret = ly_out_new_memory(&buf, 0, &out2), cleanup);
+        LY_CHECK_GOTO(ret = lyb_print_data(out2, anydata->value.tree, LYDP_WITHSIBLINGS), cleanup);
+
+        len = lyd_lyb_data_length(buf);
+        assert(len != -1);
+        str = buf;
+    } else if (anydata->value_type == LYD_ANYDATA_LYB) {
+        len = lyd_lyb_data_length(anydata->value.mem);
+        assert(len != -1);
+        str = anydata->value.mem;
+    } else {
+        len = strlen(anydata->value.str);
+        str = anydata->value.str;
+    }
+
+    /* followed by the content */
+    LY_CHECK_GOTO(ret = lyb_write_string(str, (size_t)len, 0, out, lybctx), cleanup);
+
+cleanup:
+    ly_out_free(out2, NULL, 1);
+    return ret;
+}
+
+/**
+ * @brief Print term node.
+ *
+ * @param[in] term Node to print.
+ * @param[in] out Out structure.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_term(struct lyd_node_term *term, struct ly_out *out, struct lyd_lyb_ctx *lybctx)
+{
+    LY_ERR ret;
+    int dynamic;
+    const char *str;
+
+    /* get value */
+    str = lyd_value2str(term, &dynamic);
+
+    /* print it */
+    ret = lyb_write_string(str, 0, 0, out, lybctx);
+
+    if (dynamic) {
+        free((char *)str);
+    }
+    return ret;
+}
+
+/**
+ * @brief Print YANG node metadata.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Data node whose metadata to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_metadata(struct ly_out *out, const struct lyd_node *node, struct lyd_lyb_ctx *lybctx)
+{
+    LY_ERR ret;
+    int dynamic;
+    uint8_t count = 0;
+    const struct lys_module *wd_mod = NULL;
+    struct lyd_meta *iter;
+    const char *str;
+
+    /* with-defaults */
+    if (node->schema->nodetype & LYD_NODE_TERM) {
+        if (((node->flags & LYD_DEFAULT) && (lybctx->options & (LYDP_WD_ALL_TAG | LYDP_WD_IMPL_TAG))) ||
+                ((lybctx->options & LYDP_WD_ALL_TAG) && ly_is_default(node))) {
+            /* we have implicit OR explicit default node, print attribute only if context include with-defaults schema */
+            wd_mod = ly_ctx_get_module_latest(node->schema->module->ctx, "ietf-netconf-with-defaults");
+        }
+    }
+
+    /* count metadata */
+    if (wd_mod) {
+        ++count;
+    }
+    for (iter = node->meta; iter; iter = iter->next) {
+        if (count == UINT8_MAX) {
+            LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of data node metadata is %u.", UINT8_MAX);
+            return LY_EINT;
+        }
+        ++count;
+    }
+
+    /* write number of metadata on 1 byte */
+    LY_CHECK_RET(lyb_write(out, &count, 1, lybctx));
+
+    if (wd_mod) {
+        /* write the "default" metadata */
+        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
+        LY_CHECK_RET(lyb_print_model(out, wd_mod, lybctx));
+        LY_CHECK_RET(lyb_write_string("default", 0, 1, out, lybctx));
+        LY_CHECK_RET(lyb_write_string("true", 0, 0, out, lybctx));
+        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+    }
+
+    /* write all the node metadata */
+    LY_LIST_FOR(node->meta, iter) {
+        /* each metadata is a subtree */
+        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
+
+        /* model */
+        LY_CHECK_RET(lyb_print_model(out, iter->annotation->module, lybctx));
+
+        /* annotation name with length */
+        LY_CHECK_RET(lyb_write_string(iter->name, 0, 1, out, lybctx));
+
+        /* get the value */
+        str = lyd_meta2str(iter, &dynamic);
+
+        /* metadata value */
+        ret = lyb_write_string(str, 0, 0, out, lybctx);
+        if (dynamic) {
+            free((char *)str);
+        }
+        LY_CHECK_RET(ret);
+
+        /* finish metadata subtree */
+        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print opaque node attributes.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Opaque node whose attributes to print.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_attributes(struct ly_out *out, const struct lyd_node_opaq *node, struct lyd_lyb_ctx *lybctx)
+{
+    uint8_t count = 0;
+    struct ly_attr *iter;
+
+    for (iter = node->attr; iter; iter = iter->next) {
+        if (count == UINT8_MAX) {
+            LOGERR(lybctx->ctx, LY_EINT, "Maximum supported number of data node attributes is %u.", UINT8_MAX);
+            return LY_EINT;
+        }
+        ++count;
+    }
+
+    /* write number of attributes on 1 byte */
+    LY_CHECK_RET(lyb_write(out, &count, 1, lybctx));
+
+    /* write all the attributes */
+    LY_LIST_FOR(node->attr, iter) {
+        /* each attribute is a subtree */
+        LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
+
+        /* prefix */
+        LY_CHECK_RET(lyb_write_string(iter->prefix.pref, 0, 1, out, lybctx));
+
+        /* namespace */
+        LY_CHECK_RET(lyb_write_string(iter->prefix.ns, 0, 1, out, lybctx));
+
+        /* name */
+        LY_CHECK_RET(lyb_write_string(iter->name, 0, 1, out, lybctx));
+
+        /* value prefixes */
+        LY_CHECK_RET(lyb_print_opaq_prefixes(out, iter->val_prefs, lybctx));
+
+        /* format */
+        LY_CHECK_RET(lyb_write_number(iter->format, 1, out, lybctx));
+
+        /* value */
+        LY_CHECK_RET(lyb_write_string(iter->value, 0, 0, out, lybctx));
+
+        /* finish attribute subtree */
+        LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print schema node hash.
+ *
+ * @param[in] out Out structure.
+ * @param[in] schema Schema node whose hash to print.
+ * @param[in,out] sibling_ht Cached hash table for these siblings, created if NULL.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_schema_hash(struct ly_out *out, struct lysc_node *schema, struct hash_table **sibling_ht, struct lyd_lyb_ctx *lybctx)
+{
+    LY_ARRAY_SIZE_TYPE u;
+    uint32_t i;
+    LYB_HASH hash;
+    struct lyd_lyb_sib_ht *sib_ht;
+    struct lysc_node *first_sibling;
+
+    if (!schema) {
+        /* opaque node, write empty hash */
+        hash = 0;
+        LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx));
+        return LY_SUCCESS;
+    }
+
+    /* create whole sibling HT if not already created and saved */
+    if (!*sibling_ht) {
+        /* get first schema data sibling (or input/output) */
+        first_sibling = (struct lysc_node *)lys_getnext(NULL, lysc_data_parent(schema), schema->module->compiled, 0);
+        LY_ARRAY_FOR(lybctx->sib_hts, u) {
+            if (lybctx->sib_hts[u].first_sibling == first_sibling) {
+                /* we have already created a hash table for these siblings */
+                *sibling_ht = lybctx->sib_hts[u].ht;
+                break;
+            }
+        }
+
+        if (!*sibling_ht) {
+            /* we must create sibling hash table */
+            LY_CHECK_RET(lyb_hash_siblings(first_sibling, sibling_ht));
+
+            /* and save it */
+            LY_ARRAY_NEW_RET(lybctx->ctx, lybctx->sib_hts, sib_ht, LY_EMEM);
+
+            sib_ht->first_sibling = first_sibling;
+            sib_ht->ht = *sibling_ht;
+        }
+    }
+
+    /* get our hash */
+    LY_CHECK_RET(lyb_hash_find(*sibling_ht, schema, &hash));
+
+    /* write the hash */
+    LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx));
+
+    if (hash & LYB_HASH_COLLISION_ID) {
+        /* no collision for this hash, we are done */
+        return LY_SUCCESS;
+    }
+
+    /* written hash was a collision, write also all the preceding hashes */
+    for (i = 0; !(hash & (LYB_HASH_COLLISION_ID >> i)); ++i);
+
+    for (; i; --i) {
+        hash = lyb_hash(schema, i - 1);
+        if (!hash) {
+            return LY_EINT;
+        }
+        assert(hash & (LYB_HASH_COLLISION_ID >> (i - 1)));
+
+        LY_CHECK_RET(lyb_write(out, &hash, sizeof hash, lybctx));
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Print data subtree.
+ *
+ * @param[in] out Out structure.
+ * @param[in] node Root node of the subtree to print.
+ * @param[in,out] sibling_ht Cached hash table for these data siblings, created if NULL.
+ * @param[in] lybctx LYB context.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_print_subtree(struct ly_out *out, const struct lyd_node *node, struct hash_table **sibling_ht, struct lyd_lyb_ctx *lybctx)
+{
+    struct hash_table *child_ht = NULL;
+
+    /* register a new subtree */
+    LY_CHECK_RET(lyb_write_start_subtree(out, lybctx));
+
+    /* write model info first */
+    if (!node->schema && !((struct lyd_node_opaq *)node)->parent) {
+        LY_CHECK_RET(lyb_print_model(out, NULL, lybctx));
+    } else if (node->schema && !lysc_data_parent(node->schema)) {
+        LY_CHECK_RET(lyb_print_model(out, node->schema->module, lybctx));
+    }
+
+    /* write schema hash */
+    LY_CHECK_RET(lyb_print_schema_hash(out, (struct lysc_node *)node->schema, sibling_ht, lybctx));
+
+    /* write any metadata/attributes */
+    if (node->schema) {
+        LY_CHECK_RET(lyb_print_metadata(out, node, lybctx));
+    } else {
+        LY_CHECK_RET(lyb_print_attributes(out, (struct lyd_node_opaq *)node, lybctx));
+    }
+
+    /* write node content */
+    if (!node->schema) {
+        LY_CHECK_RET(lyb_print_opaq((struct lyd_node_opaq *)node, out, lybctx));
+    } else if (node->schema->nodetype & LYD_NODE_INNER) {
+        /* nothing to write */
+    } else if (node->schema->nodetype & LYD_NODE_TERM) {
+        LY_CHECK_RET(lyb_print_term((struct lyd_node_term *)node, out, lybctx));
+    } else if (node->schema->nodetype & LYD_NODE_ANY) {
+        LY_CHECK_RET(lyb_print_anydata((struct lyd_node_any *)node, out, lybctx));
+    } else {
+        LOGINT_RET(lybctx->ctx);
+    }
+
+    /* recursively write all the descendants */
+    LY_LIST_FOR(lyd_node_children(node, 0), node) {
+        LY_CHECK_RET(lyb_print_subtree(out, node, &child_ht, lybctx));
+    }
+
+    /* finish this subtree */
+    LY_CHECK_RET(lyb_write_stop_subtree(out, lybctx));
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyb_print_data(struct ly_out *out, const struct lyd_node *root, int options)
+{
+    LY_ERR ret = LY_SUCCESS;
+    uint8_t zero = 0;
+    LY_ARRAY_SIZE_TYPE u;
+    struct hash_table *top_sibling_ht = NULL;
+    const struct lys_module *prev_mod = NULL;
+    struct lyd_lyb_ctx lybctx = {0};
+
+    lybctx.options = options;
+    if (root) {
+        lybctx.ctx = LYD_NODE_CTX(root);
+
+        if (root->schema && lysc_data_parent(root->schema)) {
+            LOGERR(lybctx.ctx, LY_EINVAL, "LYB printer supports only printing top-level nodes.");
+            return LY_EINVAL;
+        }
+    }
+
+    /* LYB magic number */
+    LY_CHECK_GOTO(ret = lyb_print_magic_number(out, &lybctx), cleanup);
+
+    /* LYB header */
+    LY_CHECK_GOTO(ret = lyb_print_header(out, &lybctx), cleanup);
+
+    /* all used models */
+    LY_CHECK_GOTO(ret = lyb_print_data_models(out, root, &lybctx), cleanup);
+
+    LY_LIST_FOR(root, root) {
+        /* do not reuse sibling hash tables from different modules */
+        if (!root->schema || (root->schema->module != prev_mod)) {
+            top_sibling_ht = NULL;
+            prev_mod = root->schema ? root->schema->module : NULL;
+        }
+
+        LY_CHECK_GOTO(ret = lyb_print_subtree(out, root, &top_sibling_ht, &lybctx), cleanup);
+
+        if (!(options & LYDP_WITHSIBLINGS)) {
+            break;
+        }
+    }
+
+    /* ending zero byte */
+    LY_CHECK_GOTO(ret = lyb_write(out, &zero, sizeof zero, &lybctx), cleanup);
+
+cleanup:
+    LY_ARRAY_FREE(lybctx.subtrees);
+    LY_ARRAY_FOR(lybctx.sib_hts, u) {
+        lyht_free(lybctx.sib_hts[u].ht);
+    }
+    LY_ARRAY_FREE(lybctx.sib_hts);
+
+    return ret;
+}
diff --git a/src/printer_xml.c b/src/printer_xml.c
index d63b16c..d143ba7 100644
--- a/src/printer_xml.c
+++ b/src/printer_xml.c
@@ -243,6 +243,7 @@
                 pref = xml_print_ns(ctx, attr->prefix.ns, attr->prefix.pref, 0);
                 break;
             case LYD_SCHEMA:
+            case LYD_LYB:
                 /* cannot be created */
                 LOGINT(node->ctx);
                 return LY_EINT;
@@ -275,6 +276,7 @@
         xml_print_ns(ctx, node->prefix.ns, NULL, 0);
         break;
     case LYD_SCHEMA:
+    case LYD_LYB:
         /* cannot be created */
         LOGINT(node->ctx);
         return LY_EINT;
@@ -364,7 +366,7 @@
 {
     struct lyd_node_any *any = (struct lyd_node_any *)node;
     struct lyd_node *iter;
-    int prev_opts;
+    int prev_opts, prev_lo;
     LY_ERR ret;
 
     xml_print_node_open(ctx, (struct lyd_node *)node);
@@ -375,6 +377,22 @@
         ly_print(ctx->out, "/>%s", LEVEL ? "\n" : "");
         return LY_SUCCESS;
     } else {
+        if (any->value_type == LYD_ANYDATA_LYB) {
+            /* turn logging off */
+            prev_lo = ly_log_options(0);
+
+            /* try to parse it into a data tree */
+            iter = lyd_parse_mem((struct ly_ctx *)LYD_NODE_CTX(node), any->value.mem, LYD_LYB,
+                                 LYD_OPT_PARSE_ONLY | LYD_OPT_OPAQ | LYD_OPT_STRICT);
+            ly_log_options(prev_lo);
+            if (!ly_errcode(LYD_NODE_CTX(node))) {
+                /* successfully parsed */
+                free(any->value.mem);
+                any->value.tree = iter;
+                any->value_type = LYD_ANYDATA_DATATREE;
+            }
+        }
+
         switch (any->value_type) {
         case LYD_ANYDATA_DATATREE:
             /* close opening tag and print data */
@@ -408,11 +426,9 @@
             ly_print(ctx->out, ">%s", any->value.str);
             break;
         case LYD_ANYDATA_JSON:
-#if 0 /* TODO LYB format */
         case LYD_ANYDATA_LYB:
-#endif
             /* JSON and LYB format is not supported */
-            LOGWRN(node->schema->module->ctx, "Unable to print anydata content (type %d) as XML.", any->value_type);
+            LOGWRN(LYD_NODE_CTX(node), "Unable to print anydata content (type %d) as XML.", any->value_type);
             goto no_content;
         }
 
diff --git a/src/tree_data.c b/src/tree_data.c
index 9a905ac..eceaebf 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -323,10 +323,10 @@
     case LYD_JSON:
         lyd_parse_json(ctx, data, options, trees, &result);
         break;
-    case LYD_LYB:
-        lyd_parse_lyb(ctx, data, options, trees, &result);
-        break;
 #endif
+    case LYD_LYB:
+        lyd_parse_lyb_data(ctx, data, options, &result, NULL);
+        break;
     case LYD_SCHEMA:
         LOGINT(ctx);
         break;
@@ -1818,6 +1818,7 @@
             }
             break;
         case LYD_SCHEMA:
+        case LYD_LYB:
             /* not allowed */
             LOGINT(LYD_NODE_CTX(node1));
             return LY_EINT;
@@ -1928,15 +1929,13 @@
                     return LY_ENOT;
                 }
                 return LY_SUCCESS;
-    #if 0 /* TODO LYB format */
             case LYD_ANYDATA_LYB:
-                int len1 = lyd_lyb_data_length(any1->value.mem);
-                int len2 = lyd_lyb_data_length(any2->value.mem);
+                len1 = lyd_lyb_data_length(any1->value.mem);
+                len2 = lyd_lyb_data_length(any2->value.mem);
                 if (len1 != len2 || memcmp(any1->value.mem, any2->value.mem, len1)) {
                     return LY_ENOT;
                 }
                 return LY_SUCCESS;
-    #endif
             }
         }
     }
@@ -1984,6 +1983,7 @@
                   struct lyd_node **dup_p)
 {
     LY_ERR ret;
+    int len;
     struct lyd_node *dup = NULL;
     LY_ARRAY_SIZE_TYPE u;
 
@@ -2108,7 +2108,7 @@
                     ret = ei ? ei->prev->no : LY_EOTHER;
                     goto error;
                 }
-                LY_CHECK_ERR_GOTO(!any->value.tree, ret = 0 ,error);
+                LY_CHECK_ERR_GOTO(!any->value.tree, ret = 0, error);
             }
             break;
         case LYD_ANYDATA_STRING:
@@ -2118,6 +2118,14 @@
                 any->value.str = lydict_insert(LYD_NODE_CTX(node), orig->value.str, strlen(orig->value.str));
             }
             break;
+        case LYD_ANYDATA_LYB:
+            if (orig->value.mem) {
+                len = lyd_lyb_data_length(orig->value.mem);
+                any->value.mem = malloc(len);
+                LY_CHECK_ERR_GOTO(!any->value.mem, LOGMEM(LYD_NODE_CTX(node)); ret = LY_EMEM, error);
+                memcpy(any->value.mem, orig->value.mem, len);
+            }
+            break;
         }
     }
 
diff --git a/src/tree_data.h b/src/tree_data.h
index d218490..ff040f9 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -122,9 +122,7 @@
     LYD_SCHEMA = 0,      /**< invalid instance data format, value prefixes map to YANG import prefixes */
     LYD_XML,             /**< XML instance data format, value prefixes map to XML namespace prefixes */
     LYD_JSON,            /**< JSON instance data format, value prefixes map to module names */
-#if 0
-    LYD_LYB,             /**< LYB format of the instance data */
-#endif
+    LYD_LYB,             /**< LYB instance data format, invalid value prefix format (same as LYD_JSON) */
 } LYD_FORMAT;
 
 /**
@@ -139,9 +137,7 @@
                                           is printed in XML format. */
     LYD_ANYDATA_XML,                 /**< Value is a string containing the serialized XML data. */
     LYD_ANYDATA_JSON,                /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */
-#if 0 /* TODO LYB format */
     LYD_ANYDATA_LYB,                 /**< Value is a memory chunk with the serialized data tree in LYB format. */
-#endif
 } LYD_ANYDATA_VALUETYPE;
 
 /** @} */
@@ -474,6 +470,9 @@
 #define LYD_OPT_OPAQ            0x0008  /**< Instead of silently ignoring data without definition, parse them into
                                              an opaq node. Do not combine with #LYD_OPT_STRICT. */
 #define LYD_OPT_NO_STATE        0x0010  /**< Forbid state data in the parsed data. */
+#define LYD_OPT_LYB_MOD_UPDATE  0x0020  /**< Only for LYB format, allow parsing data printed using a specific module
+                                             revision to be loaded even with a module with the same name but newer
+                                             revision. */
 
 #define LYD_OPT_MASK            0xFFFF  /**< Mask for all the parser options. */
 
@@ -601,6 +600,15 @@
 struct lyd_node *lyd_parse_path(struct ly_ctx *ctx, const char *path, LYD_FORMAT format, int options);
 
 /**
+ * @brief Learn the length of LYB data.
+ *
+ * @param[in] data LYB data to examine.
+ * @return Length of the LYB data chunk,
+ * @return -1 on error.
+ */
+int lyd_lyb_data_length(const char *data);
+
+/**
  * @brief Fully validate a data tree.
  *
  * @param[in,out] tree Data tree to recursively validate. May be changed by validation.
@@ -1002,7 +1010,7 @@
 #define LYD_COMPARE_DEFAULTS 0x02       /* By default, implicit and explicit default nodes are considered to be equal. This flag
                                            changes this behavior and implicit (automatically created default node) and explicit
                                            (explicitly created node with the default value) default nodes are considered different. */
-/**@} datacompareoptions */
+/** @} datacompareoptions */
 
 /**
  * @brief Compare 2 data nodes if they are equivalent.
diff --git a/src/tree_data_free.c b/src/tree_data_free.c
index 43bafee..d47e19c 100644
--- a/src/tree_data_free.c
+++ b/src/tree_data_free.c
@@ -117,6 +117,18 @@
     }
 }
 
+void
+ly_free_val_prefs(const struct ly_ctx *ctx, struct ly_prefix *val_prefs)
+{
+    LY_ARRAY_SIZE_TYPE u;
+
+    LY_ARRAY_FOR(val_prefs, u) {
+        FREE_STRING(ctx, val_prefs[u].pref);
+        FREE_STRING(ctx, val_prefs[u].ns);
+    }
+    LY_ARRAY_FREE(val_prefs);
+}
+
 /**
  * @brief Free Data (sub)tree.
  * @param[in] node Data node to be freed.
@@ -128,7 +140,6 @@
     struct lyd_node *iter, *next;
     struct lyd_node *children;
     struct lyd_node_opaq *opaq;
-    LY_ARRAY_SIZE_TYPE u;
 
     assert(node);
 
@@ -144,13 +155,7 @@
         FREE_STRING(LYD_NODE_CTX(opaq), opaq->name);
         FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.pref);
         FREE_STRING(LYD_NODE_CTX(opaq), opaq->prefix.ns);
-        if (opaq->val_prefs) {
-            LY_ARRAY_FOR(opaq->val_prefs, u) {
-                FREE_STRING(LYD_NODE_CTX(opaq), opaq->val_prefs[u].pref);
-                FREE_STRING(LYD_NODE_CTX(opaq), opaq->val_prefs[u].ns);
-            }
-            LY_ARRAY_FREE(opaq->val_prefs);
-        }
+        ly_free_val_prefs(LYD_NODE_CTX(opaq), opaq->val_prefs);
         FREE_STRING(LYD_NODE_CTX(opaq), opaq->value);
     } else if (node->schema->nodetype & LYD_NODE_INNER) {
         /* remove children hash table in case of inner data node */
@@ -172,11 +177,9 @@
         case LYD_ANYDATA_JSON:
             FREE_STRING(LYD_NODE_CTX(node), ((struct lyd_node_any *)node)->value.str);
             break;
-#if 0 /* TODO LYB format */
         case LYD_ANYDATA_LYB:
             free(((struct lyd_node_any *)node)->value.mem);
             break;
-#endif
         }
     } else if (node->schema->nodetype & LYD_NODE_TERM) {
         ((struct lysc_node_leaf *)node->schema)->type->plugin->free(LYD_NODE_CTX(node), &((struct lyd_node_term *)node)->value);
diff --git a/src/tree_data_hash.c b/src/tree_data_hash.c
index bc0a0a3..ee7855b 100644
--- a/src/tree_data_hash.c
+++ b/src/tree_data_hash.c
@@ -138,7 +138,7 @@
         unsigned int u;
 
         /* the hash table is created only when the number of children in a node exceeds the
-         * defined minimal limit LY_CACHE_HT_MIN_CHILDREN
+         * defined minimal limit LYD_HT_MIN_ITEMS
          */
         for (u = 0, iter = node->parent->child; iter; ++u, iter = iter->next);
         if (u >= LYD_HT_MIN_ITEMS) {
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index cb2cb1f..f7a2920 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -21,6 +21,7 @@
 #include "log.h"
 #include "tree.h"
 #include "tree_data.h"
+#include "tree_data_internal.h"
 #include "tree_schema.h"
 
 struct lyd_node **
@@ -158,3 +159,98 @@
 
     return LY_SUCCESS;
 }
+
+void
+lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *when_check, struct lyd_meta **meta, int options)
+{
+    struct lyd_meta *meta2, *prev_meta = NULL;
+
+    if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && node->schema->when) {
+        if (options & LYD_OPT_TRUSTED) {
+            /* just set it to true */
+            node->flags |= LYD_WHEN_TRUE;
+        } else {
+            /* remember we need to evaluate this node's when */
+            ly_set_add(when_check, node, LY_SET_OPT_USEASLIST);
+        }
+    }
+
+    if (options & LYD_OPT_TRUSTED) {
+        /* node is valid */
+        node->flags &= ~LYD_NEW;
+    }
+
+    LY_LIST_FOR(*meta, meta2) {
+        if (!strcmp(meta2->name, "default") && !strcmp(meta2->annotation->module->name, "ietf-netconf-with-defaults")
+                && meta2->value.boolean) {
+            /* node is default according to the metadata */
+            node->flags |= LYD_DEFAULT;
+
+            /* delete the metadata */
+            if (prev_meta) {
+                prev_meta->next = meta2->next;
+            } else {
+                *meta = (*meta)->next;
+            }
+            lyd_free_meta(LYD_NODE_CTX(node), meta2, 0);
+            break;
+        }
+
+        prev_meta = meta2;
+    }
+}
+
+LYB_HASH
+lyb_hash(struct lysc_node *sibling, uint8_t collision_id)
+{
+    const struct lys_module *mod;
+    int ext_len;
+    uint32_t full_hash;
+    LYB_HASH hash;
+
+    if ((collision_id < LYS_NODE_HASH_COUNT) && sibling->hash[collision_id]) {
+        return sibling->hash[collision_id];
+    }
+
+    mod = sibling->module;
+
+    full_hash = dict_hash_multi(0, mod->name, strlen(mod->name));
+    full_hash = dict_hash_multi(full_hash, sibling->name, strlen(sibling->name));
+    if (collision_id) {
+        if (collision_id > strlen(mod->name)) {
+            /* fine, we will not hash more bytes, just use more bits from the hash than previously */
+            ext_len = strlen(mod->name);
+        } else {
+            /* use one more byte from the module name than before */
+            ext_len = collision_id;
+        }
+        full_hash = dict_hash_multi(full_hash, mod->name, ext_len);
+    }
+    full_hash = dict_hash_multi(full_hash, NULL, 0);
+
+    /* use the shortened hash */
+    hash = full_hash & (LYB_HASH_MASK >> collision_id);
+    /* add colision identificator */
+    hash |= LYB_HASH_COLLISION_ID >> collision_id;
+
+    /* save this hash */
+    if (collision_id < LYS_NODE_HASH_COUNT) {
+        sibling->hash[collision_id] = hash;
+    }
+
+    return hash;
+}
+
+int
+lyb_has_schema_model(const struct lysc_node *sibling, const struct lys_module **models)
+{
+    LY_ARRAY_SIZE_TYPE u;
+
+    LY_ARRAY_FOR(models, u) {
+        if (sibling->module == models[u]) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index 439ad6c..4c91bf5 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -3,7 +3,7 @@
  * @author Radek Krejci <rkrejci@cesnet.cz>
  * @brief internal functions for YANG schema trees.
  *
- * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
  *
  * This source code is licensed under BSD 3-Clause License (the "License").
  * You may not use this file except in compliance with the License.
@@ -15,14 +15,42 @@
 #ifndef LY_TREE_DATA_INTERNAL_H_
 #define LY_TREE_DATA_INTERNAL_H_
 
-#include "tree_data.h"
+#include "lyb.h"
 #include "plugins_types.h"
+#include "set.h"
+#include "tree_data.h"
 
 #include <stddef.h>
 
 struct ly_path_predicate;
 
 /**
+ * @brief Internal data parser flags.
+ */
+#define LYD_INTOPT_RPC      0x01    /**< RPC/action invocation is being parsed */
+#define LYD_INTOPT_NOTIF    0x02    /**< notification is being parsed */
+#define LYD_INTOPT_REPLY    0x04    /**< RPC/action reply is being parsed */
+
+/**
+ * @brief Hash schema sibling to be used for LYB data.
+ *
+ * @param[in] sibling Sibling to hash.
+ * @param[in] collision_id Collision ID of the hash to generate.
+ * @return Generated hash.
+ */
+LYB_HASH lyb_hash(struct lysc_node *sibling, uint8_t collision_id);
+
+/**
+ * @brief Check whether a sibling module is in a module array.
+ *
+ * @param[in] sibling Sibling to check.
+ * @param[in] models Modules in a sized array.
+ * @return non-zero if the module was found,
+ * @return 0 if not found.
+ */
+int lyb_has_schema_model(const struct lysc_node *sibling, const struct lys_module **models);
+
+/**
  * @brief Check whether a node to be deleted is the first top-level sibling.
  *
  * @param[in] first First sibling.
@@ -318,6 +346,57 @@
 LY_ERR lyd_parse_xml_reply(const struct lyd_node *request, const char *data, struct lyd_node **tree, struct lyd_node **op);
 
 /**
+ * @brief Parse binary data as YANG data tree.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] data Pointer to the input data to parse.
+ * @param[in] options @ref dataparseroptions
+ * @param[out] tree Parsed data tree. Note that NULL can be a valid result.
+ * @param[out] parsed_bytes Optional number of parsed bytes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_lyb_data(struct ly_ctx *ctx, const char *data, int options, struct lyd_node **tree, int *parsed_bytes);
+
+/**
+ * @brief Parse binary data as YANG RPC/action invocation.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] data Pointer to the input data to parse.
+ * @param[out] tree Parsed full RPC/action tree.
+ * @param[out] op Optional pointer to the actual operation. Useful mainly for action.
+ * @param[out] parsed_bytes Optional number of parsed bytes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_lyb_rpc(struct ly_ctx *ctx, const char *data, struct lyd_node **tree, struct lyd_node **op,
+                         int *parsed_bytes);
+
+/**
+ * @brief Parse binary data as YANG notification.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] data Pointer to the input data to parse.
+ * @param[out] tree Parsed full notification tree.
+ * @param[out] op Optional pointer to the actual notification. Useful mainly for nested notifications.
+ * @param[out] parsed_bytes Optional number of parsed bytes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_lyb_notif(struct ly_ctx *ctx, const char *data, struct lyd_node **tree, struct lyd_node **ntf,
+                           int *parsed_bytes);
+
+/**
+ * @brief Parse binary data as YANG RPC/action reply.
+ *
+ * @param[in] request Data tree of the RPC/action request.
+ * @param[in] data Pointer to the input data to parse.
+ * @param[out] tree Parsed full reply tree. It always includes duplicated operation and parents of the @p request.
+ * @param[out] op Optional pointer to the reply operation. Useful mainly for action.
+ * @param[out] parsed_bytes Optional number of parsed bytes.
+ * @return LY_ERR value.
+ */
+LY_ERR lyd_parse_lyb_reply(struct lyd_node *request, const char *data, struct lyd_node **tree, struct lyd_node **op,
+                           int *parsed_bytes);
+
+/**
  * @defgroup datahash Data nodes hash manipulation
  * @ingroup datatree
  */
@@ -416,4 +495,22 @@
  */
 LY_ERR lyd_parse_check_keys(struct lyd_node *node);
 
+/**
+ * @brief Set data flags for a newly parsed node.
+ *
+ * @param[in] node Node to use.
+ * @param[in] when_check Set of nodes with unresolved when.
+ * @param[in,out] meta Node metadata, may be removed from.
+ * @param[in] options Parse options.
+ */
+void lyd_parse_set_data_flags(struct lyd_node *node, struct ly_set *when_check, struct lyd_meta **meta, int options);
+
+/**
+ * @brief Free value prefixes.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] val_prefis Value prefixes to free, sized array (@ref sizedarrays).
+ */
+void ly_free_val_prefs(const struct ly_ctx *ctx, struct ly_prefix *val_prefs);
+
 #endif /* LY_TREE_DATA_INTERNAL_H_ */
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 8b7f7b2..1be7c3f 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1398,9 +1398,15 @@
     struct lysc_must *musts;         /**< list of must restrictions ([sized array](@ref sizedarrays)) */
 };
 
+/**
+ * @brief Maximum number of hashes stored in a schema node.
+ */
+#define LYS_NODE_HASH_COUNT 4
+
 struct lysc_action {
     uint16_t nodetype;               /**< LYS_RPC or LYS_ACTION */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_action *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node - RPC) */
@@ -1423,6 +1429,7 @@
 struct lysc_notif {
     uint16_t nodetype;               /**< LYS_NOTIF */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_notif *sp;           /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1444,6 +1451,7 @@
 struct lysc_node {
     uint16_t nodetype;               /**< type of the node (mandatory) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or
                                           in case of implicit case node. */
@@ -1464,6 +1472,7 @@
 struct lysc_node_container {
     uint16_t nodetype;               /**< LYS_CONTAINER */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1488,6 +1497,7 @@
 struct lysc_node_case {
     uint16_t nodetype;               /**< LYS_CASE */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser, unused */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1510,6 +1520,7 @@
 struct lysc_node_choice {
     uint16_t nodetype;               /**< LYS_CHOICE */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser, unused */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1534,6 +1545,7 @@
 struct lysc_node_leaf {
     uint16_t nodetype;               /**< LYS_LEAF */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1560,6 +1572,7 @@
 struct lysc_node_leaflist {
     uint16_t nodetype;               /**< LYS_LEAFLIST */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1590,6 +1603,7 @@
 struct lysc_node_list {
     uint16_t nodetype;               /**< LYS_LIST */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
@@ -1618,6 +1632,7 @@
 struct lysc_node_anydata {
     uint16_t nodetype;               /**< LYS_ANYXML or LYS_ANYDATA */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
+    uint8_t hash[LYS_NODE_HASH_COUNT]; /**< schema hash required for LYB printer/parser */
     struct lys_module *module;       /**< module structure */
     struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
     struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
diff --git a/src/xpath.c b/src/xpath.c
index 2b2dcbc..fae13dc 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -337,6 +337,7 @@
     const char *value_str;
     int dynamic;
     const struct lyd_node *child;
+    struct lyd_node *tree;
     struct lyd_node_any *any;
     LY_ERR rc;
 
@@ -409,6 +410,18 @@
         } else {
             struct ly_out *out;
 
+            if (any->value_type == LYD_ANYDATA_LYB) {
+                /* try to parse it into a data tree */
+                tree = lyd_parse_mem((struct ly_ctx *)LYD_NODE_CTX(node), any->value.mem, LYD_LYB,
+                                     LYD_OPT_PARSE_ONLY | LYD_OPT_STRICT);
+                if (!ly_errcode(LYD_NODE_CTX(node))) {
+                    /* successfully parsed */
+                    free(any->value.mem);
+                    any->value.tree = tree;
+                    any->value_type = LYD_ANYDATA_DATATREE;
+                }
+            }
+
             switch (any->value_type) {
             case LYD_ANYDATA_STRING:
             case LYD_ANYDATA_XML:
@@ -422,9 +435,9 @@
                 ly_out_free(out, NULL, 0);
                 LY_CHECK_RET(rc < 0, -rc);
                 break;
-            /* TODO case LYD_ANYDATA_LYB:
+            case LYD_ANYDATA_LYB:
                 LOGERR(LYD_NODE_CTX(node), LY_EINVAL, "Cannot convert LYB anydata into string.");
-                return -1;*/
+                return LY_EINVAL;
             }
         }
 
@@ -4226,6 +4239,7 @@
             rc = asprintf(&str, "%s:%s", mod->name, name);
             break;
         case LYD_XML:
+        case LYD_LYB:
             LOGINT_RET(set->ctx);
         }
         LY_CHECK_ERR_RET(rc == -1, LOGMEM(set->ctx), LY_EMEM);
@@ -5261,6 +5275,7 @@
             free(str);
             break;
         case LYD_XML:
+        case LYD_LYB:
             LOGINT_RET(set->ctx);
         }
 
@@ -6916,6 +6931,7 @@
                                         lydjson_resolve_prefix, NULL, LYD_JSON, predicates, pred_type);
         break;
     case LYD_XML:
+    case LYD_LYB:
         ret = LY_EINT;
         goto cleanup;
     }