Merge branch 'ly2_type_plugins' into libyang2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f83c49..ef4085a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -102,10 +102,13 @@
     src/plugins_types/leafref.c
     src/plugins_types/string.c
     src/plugins_types/union.c
+    src/plugins_types/ipv4_address.c
+    src/plugins_types/ipv4_address_no_zone.c
     src/plugins_types/ipv6_address.c
-    src/plugins_types/ip_prefix.c
+    src/plugins_types/ipv6_address_no_zone.c
+    src/plugins_types/ipv4_prefix.c
+    src/plugins_types/ipv6_prefix.c
     src/plugins_types/date_and_time.c
-    src/plugins_types/hex_string.c
     src/plugins_types/xpath1.0.c)
 
 set(libsrc
diff --git a/src/common.c b/src/common.c
index 9c0d15a..a434235 100644
--- a/src/common.c
+++ b/src/common.c
@@ -19,6 +19,7 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -46,12 +47,8 @@
 char *
 ly_strnchr(const char *s, int c, size_t len)
 {
-    for ( ; *s != (char)c; ++s, --len) {
-        if ((*s == '\0') || (!len)) {
-            return NULL;
-        }
-    }
-    return (char *)s;
+    for ( ; len && (*s != (char)c); ++s, --len) {}
+    return len ? (char *)s : NULL;
 }
 
 int
@@ -66,6 +63,44 @@
     }
 }
 
+#define LY_OVERFLOW_ADD(MAX, X, Y) ((X > MAX - Y) ? 1 : 0)
+
+#define LY_OVERFLOW_MUL(MAX, X, Y) ((X > MAX / Y) ? 1 : 0)
+
+LY_ERR
+ly_strntou8(const char *nptr, size_t len, uint8_t *ret)
+{
+    uint8_t num = 0, dig, dec_pow;
+
+    if (len > 3) {
+        /* overflow for sure */
+        return LY_EDENIED;
+    }
+
+    dec_pow = 1;
+    for ( ; len && isdigit(nptr[len - 1]); --len) {
+        dig = nptr[len - 1] - 48;
+
+        if (LY_OVERFLOW_MUL(UINT8_MAX, dig, dec_pow)) {
+            return LY_EDENIED;
+        }
+        dig *= dec_pow;
+
+        if (LY_OVERFLOW_ADD(UINT8_MAX, num, dig)) {
+            return LY_EDENIED;
+        }
+        num += dig;
+
+        dec_pow *= 10;
+    }
+
+    if (len) {
+        return LY_EVALID;
+    }
+    *ret = num;
+    return LY_SUCCESS;
+}
+
 uint32_t
 ly_value_prefix_next(const char *str_begin, const char *str_end, ly_bool *is_prefix, const char **str_next)
 {
@@ -293,7 +328,7 @@
     size_t len = 0;
     const char *ptr = str;
 
-    while (*ptr && (size_t)(ptr - str) < bytes) {
+    while (((size_t)(ptr - str) < bytes) && *ptr) {
         ++len;
         ptr += utf8_char_length_table[((unsigned char)(*ptr))];
     }
@@ -402,60 +437,81 @@
 LY_ERR
 ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t max, int base, int64_t *ret)
 {
-    char *strptr;
+    LY_ERR rc = LY_SUCCESS;
+    char *ptr, *str;
     int64_t i;
 
     LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
 
-    /* convert to 64-bit integer, all the redundant characters are handled */
-    errno = 0;
-    strptr = NULL;
+    /* duplicate the value */
+    str = strndup(val_str, val_len);
+    LY_CHECK_RET(!str, LY_EMEM);
 
-    /* parse the value */
-    i = strtoll(val_str, &strptr, base);
-    if (errno || (strptr == val_str)) {
-        return LY_EVALID;
+    /* parse the value to avoid accessing following bytes */
+    errno = 0;
+    i = strtoll(str, &ptr, base);
+    if (errno || (ptr == str)) {
+        /* invalid string */
+        rc = LY_EVALID;
     } else if ((i < min) || (i > max)) {
-        return LY_EDENIED;
-    } else if (strptr && *strptr) {
-        while (isspace(*strptr)) {
-            ++strptr;
+        /* invalid number */
+        rc = LY_EDENIED;
+    } else if (*ptr) {
+        while (isspace(*ptr)) {
+            ++ptr;
         }
-        if (*strptr && (strptr < val_str + val_len)) {
-            return LY_EVALID;
+        if (*ptr) {
+            /* invalid characters after some number */
+            rc = LY_EVALID;
         }
     }
 
-    *ret = i;
-    return LY_SUCCESS;
+    /* cleanup */
+    free(str);
+    if (!rc) {
+        *ret = i;
+    }
+    return rc;
 }
 
 LY_ERR
 ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base, uint64_t *ret)
 {
-    char *strptr;
+    LY_ERR rc = LY_SUCCESS;
+    char *ptr, *str;
     uint64_t u;
 
-    LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
+    LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
 
+    /* duplicate the value to avoid accessing following bytes */
+    str = strndup(val_str, val_len);
+    LY_CHECK_RET(!str, LY_EMEM);
+
+    /* parse the value */
     errno = 0;
-    strptr = NULL;
-    u = strtoull(val_str, &strptr, base);
-    if (errno || (strptr == val_str)) {
-        return LY_EVALID;
-    } else if ((u > max) || (u && (val_str[0] == '-'))) {
-        return LY_EDENIED;
-    } else if (strptr && *strptr) {
-        while (isspace(*strptr)) {
-            ++strptr;
+    u = strtoull(str, &ptr, base);
+    if (errno || (ptr == str)) {
+        /* invalid string */
+        rc = LY_EVALID;
+    } else if ((u > max) || (u && (str[0] == '-'))) {
+        /* invalid number */
+        rc = LY_EDENIED;
+    } else if (*ptr) {
+        while (isspace(*ptr)) {
+            ++ptr;
         }
-        if (*strptr && (strptr < val_str + val_len)) {
-            return LY_EVALID;
+        if (*ptr) {
+            /* invalid characters after some number */
+            rc = LY_EVALID;
         }
     }
 
-    *ret = u;
-    return LY_SUCCESS;
+    /* cleanup */
+    free(str);
+    if (!rc) {
+        *ret = u;
+    }
+    return rc;
 }
 
 /**
diff --git a/src/common.h b/src/common.h
index 6475c22..7a08f97 100644
--- a/src/common.h
+++ b/src/common.h
@@ -285,8 +285,8 @@
 #define LY_VCODE_NOKEY          LYVE_DATA, "List instance is missing its key \"%s\"."
 
 #define LY_ERRMSG_NOPATTERN /* LYVE_DATA */ "Unsatisfied pattern - \"%.*s\" does not conform to %s\"%s\"."
-#define LY_ERRMSG_NOLENGTH /* LYVE_DATA */ "Unsatisfied length - string length \"%s\" is not allowed."
-#define LY_ERRMSG_NORANGE /* LYVE_DATA */ "Unsatisfied range - value \"%s\" is out of the allowed range."
+#define LY_ERRMSG_NOLENGTH /* LYVE_DATA */ "Unsatisfied length - string \"%.*s\" length is not allowed."
+#define LY_ERRMSG_NORANGE /* LYVE_DATA */ "Unsatisfied range - value \"%.*s\" is out of the allowed range."
 
 /* RFC 7950 section 15 errors */
 #define LY_VCODE_NOUNIQ         LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
@@ -385,6 +385,19 @@
 int ly_strncmp(const char *refstr, const char *str, size_t str_len);
 
 /**
+ * @brief Similar functionality to strtoul() except number length in the string
+ * must be specified and the whole number must be parsed for success.
+ *
+ * @param[in] nptr Number string.
+ * @param[in] len Number string length starting at @p nptr.
+ * @param[out] ret Parsed number.
+ * @return LY_EDENIED on overflow.
+ * @return LY_EVALID on encountering a non-digit character.
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR ly_strntou8(const char *nptr, size_t len, uint8_t *ret);
+
+/**
  * @brief Similar to strlen(3) but accepts NULL and returns 0.
  *
  * @param[in] s String to examine.
diff --git a/src/plugins.c b/src/plugins.c
index d0b28a0..85e3745 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -54,14 +54,17 @@
 /*
  * ietf-inet-types
  */
-extern const struct lyplg_type_record plugins_ip_address[];
-extern const struct lyplg_type_record plugins_ip_prefix[];
+extern const struct lyplg_type_record plugins_ipv4_address[];
+extern const struct lyplg_type_record plugins_ipv4_address_no_zone[];
+extern const struct lyplg_type_record plugins_ipv6_address[];
+extern const struct lyplg_type_record plugins_ipv6_address_no_zone[];
+extern const struct lyplg_type_record plugins_ipv4_prefix[];
+extern const struct lyplg_type_record plugins_ipv6_prefix[];
 
 /*
  * ietf-yang-types
  */
 extern const struct lyplg_type_record plugins_date_and_time[];
-extern const struct lyplg_type_record plugins_hex_string[];
 extern const struct lyplg_type_record plugins_xpath10[];
 
 /*
@@ -420,12 +423,15 @@
     LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_union), error);
 
     /* ietf-inet-types */
-    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ip_address), error);
-    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ip_prefix), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_prefix), error);
+    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_prefix), error);
 
     /* ietf-yang-types */
     LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
-    LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_hex_string), error);
     LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
 
     /* internal extensions */
diff --git a/src/plugins_types.c b/src/plugins_types.c
index f4790f9..9ea7fcb 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -459,8 +459,8 @@
         memset(&valcopy[len], '0', fraction_digits);
     }
 
-    ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807),
-            valcopy, len, &d, err);
+    ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1),
+            INT64_C(9223372036854775807), valcopy, size - 1, &d, err);
     if (!ret_val && ret) {
         *ret = d;
     }
@@ -518,7 +518,7 @@
 
 API LY_ERR
 lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
-        struct ly_err_item **err)
+        size_t strval_len, struct ly_err_item **err)
 {
     LY_ARRAY_COUNT_TYPE u;
     ly_bool is_length; /* length or range */
@@ -535,7 +535,7 @@
                     return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
                 } else {
                     return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
-                            is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, strval);
+                            is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
                 }
             } else if ((uint64_t)value <= range->parts[u].max_u64) {
                 /* inside the range */
@@ -547,7 +547,7 @@
                     return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
                 } else {
                     return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
-                            is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, strval);
+                            is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
                 }
             }
         } else {
@@ -557,7 +557,7 @@
                 if (range->emsg) {
                     return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
                 } else {
-                    return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, strval);
+                    return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
                 }
             } else if (value <= range->parts[u].max_64) {
                 /* inside the range */
@@ -568,7 +568,7 @@
                 if (range->emsg) {
                     return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
                 } else {
-                    return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, strval);
+                    return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
                 }
             }
         }
@@ -702,18 +702,17 @@
 
     LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL);
 
+    *path = NULL;
     *err = NULL;
 
     switch (format) {
-    case LY_VALUE_CANON:
-        LOGARG(ctx, format);
-        return LY_EINVAL;
     case LY_VALUE_SCHEMA:
     case LY_VALUE_SCHEMA_RESOLVED:
     case LY_VALUE_XML:
-    case LY_VALUE_LYB:
         prefix_opt = LY_PATH_PREFIX_MANDATORY;
         break;
+    case LY_VALUE_CANON:
+    case LY_VALUE_LYB:
     case LY_VALUE_JSON:
         prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
         break;
@@ -770,7 +769,7 @@
 }
 
 API LY_ERR
-lyplg_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *der)
+lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *der)
 {
     LY_ARRAY_COUNT_TYPE u;
 
diff --git a/src/plugins_types.h b/src/plugins_types.h
index d104b45..3e29f16 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -39,6 +39,7 @@
 struct lysc_pattern;
 struct lysc_range;
 struct lysc_type;
+struct lysc_type_bits;
 struct lysc_type_leafref;
 
 /**
@@ -248,6 +249,27 @@
 LY_ERR lyplg_type_make_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres);
 
 /**
+ * @brief Get the bitmap size of a bits value bitmap.
+ *
+ * Bitmap size is rounded up to the smallest integer size (1, 2, 4, or 8 bytes).
+ * If more than 8 bytes are needed to hold all the bit positions, no rounding is performed.
+ *
+ * @param[in] type Bits type.
+ * @return Bitmap size in bytes.
+ */
+size_t lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type);
+
+/**
+ * @brief Check whether a particular bit of a bitmap is set.
+ *
+ * @param[in] bitmap Bitmap to read from.
+ * @param[in] size Size of @p bitmap.
+ * @param[in] bit_position Bit position to check.
+ * @return Whether the bit is set or not.
+ */
+ly_bool lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position);
+
+/**
  * @brief Get format-specific prefix for a module.
  *
  * Use only in implementations of ::lyplg_type_print_clb which provide all the necessary parameters for this function.
@@ -342,10 +364,10 @@
  *
  * @{
  */
-#define LYPLG_TYPE_STORE_DYNAMIC   0x01 /**< String value was dynamically allocated and is supposed to be freed or
+#define LYPLG_TYPE_STORE_DYNAMIC   0x01 /**< Value was dynamically allocated in its exact size and is supposed to be freed or
                                              directly inserted into the context's dictionary (e.g. in case of canonization).
-                                             In any case, the caller of the callback does not free the provided string
-                                             value after calling the type's store callback with this option */
+                                             In any case, the caller of the callback does not free the provided
+                                             value after calling the type's store callback with this option. */
 #define LYPLG_TYPE_STORE_IMPLEMENT 0x02 /**< If a foreign module is needed to be implemented to successfully instantiate
                                              the value, make the module implemented. */
 /** @} plugintypestoreopts */
@@ -358,13 +380,13 @@
  * and stored on-demand. But if @p format is ::LY_VALUE_CANON (or another, which must be equal to the canonical
  * value), the canonical value should be stored so that it does not have to be generated later.
  *
- * Note that the @p value is not necessarily zero-terminated. The provided @p value_len is always correct.
- * All store functions have to free a dynamically allocated @p value in all cases (even error).
+ * Note that the @p value is not necessarily used whole (may not be zero-terminated if a string). The provided
+ * @p value_len is always correct. All store functions have to free a dynamically allocated @p value in all
+ * cases (even on error).
  *
  * @param[in] ctx libyang Context
  * @param[in] type Type of the value being stored.
- * @param[in] value Lexical representation of the value to be stored.
- *            It is never NULL, empty string is represented as "" with zero @p value_len.
+ * @param[in] value Value to be stored.
  * @param[in] value_len Length (number of bytes) of the given @p value.
  * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
  * @param[in] format Input format of the value, see the description for details.
@@ -514,38 +536,33 @@
  * @ingroup pluginsTypes
  * @{
  *
- * Simple functions implementing @ref howtoPluginsTypes callbacks handling simply just the canonical string of the value
- * (::lyd_value._canonical).
+ * Simple functions implementing @ref howtoPluginsTypes callbacks handling types that allocate no dynamic
+ * value and always generate their canonical value (::lyd_value._canonical).
  */
 
 /**
- * @brief Generic simple comparison callback checking the canonical value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for a generic simple type.
  */
 LY_ERR lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Generic simple printer callback of the canonized value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for a generic simple type.
  */
 const void *lyplg_type_print_simple(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
- * @brief Generic simple hash callback of the canonized value.
- * Implementation of the ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for a generic simple type.
  */
 const void *lyplg_type_hash_simple(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
 
 /**
- * @brief Generic simple duplication callback.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for a generic simple type.
  */
 LY_ERR lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
 /**
- * @brief Generic cleanup callback freeing only the canonized value in ::lyd_value._canonical.
- * Simple implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for a generic simple type.
  */
 void lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value);
 
@@ -556,17 +573,42 @@
  * @ingroup pluginsTypes
  * @{
  *
- * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement binary built-in type.
+ * Callbacks used to implement binary built-in type.
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in binary type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in binary type.
  */
 LY_ERR lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in binary type.
+ */
+LY_ERR lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in binary type.
+ */
+const void *lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the built-in binary type.
+ */
+const void *lyplg_type_hash_binary(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in binary type.
+ */
+LY_ERR lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in binary type.
+ */
+void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
+
 /** @} pluginsTypesBinary */
 
 /**
@@ -578,22 +620,35 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in bits type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of the ::lyplg_type_store_clb for the built-in bits type.
  */
 LY_ERR lyplg_type_store_bits(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Duplication callback of the bits values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of the ::lyplg_type_compare_clb for the built-in bits type.
+ */
+LY_ERR lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of the ::lyplg_type_print_clb for the built-in bits type.
+ */
+const void *lyplg_type_print_bits(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of the ::lyplg_type_hash_clb for the built-in bits type.
+ */
+const void *lyplg_type_hash_bits(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
+
+/**
+ * @brief Implementation of the ::lyplg_type_dup_clb for the built-in bits type.
  */
 LY_ERR lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
 /**
- * @brief Free value of the YANG built-in bits type.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of the ::lyplg_type_free_clb for the built-in bits type.
  */
 void lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value);
 
@@ -608,8 +663,7 @@
  */
 
 /**
- * @brief Validate and store value of the YANG built-in boolean type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in boolean type.
  */
 LY_ERR lyplg_type_store_boolean(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -626,8 +680,7 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in decimal64 types.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in decimal64 type.
  */
 LY_ERR lyplg_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -644,8 +697,7 @@
  */
 
 /**
- * @brief Validate and store value of the YANG built-in empty type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in empty type.
  */
 LY_ERR lyplg_type_store_empty(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -662,13 +714,18 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in enumeration type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in enumeration type.
  */
 LY_ERR lyplg_type_store_enum(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in enumeration type.
+ */
+const void *lyplg_type_print_enum(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
 /** @} pluginsTypesEnumeration */
 
 /**
@@ -680,22 +737,19 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in identiytref type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in identityref type.
  */
 LY_ERR lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Comparison callback for built-in identityref type.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in identityref type.
  */
 LY_ERR lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Printer callback printing identityref value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in identityref type.
  */
 const void *lyplg_type_print_identityref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len);
@@ -711,42 +765,36 @@
  */
 
 /**
- * @brief Validate and store value of the YANG built-in instance-identifier type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in instance-identifier type.
  */
 LY_ERR lyplg_type_store_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Comparison callback checking the instance-identifier value.
- * Implementation of the ::lyplg_type_compare_clb.
- */
-LY_ERR lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2);
-
-/**
- * @brief Printer callback printing the instance-identifier value.
- * Implementation of the ::lyplg_type_print_clb.
- */
-const void *lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
-        void *prefix_data, ly_bool *dynamic, size_t *value_len);
-
-/**
- * @brief Duplication callback of the instance-identifier values.
- * Implementation of the ::lyplg_type_dup_clb.
- */
-LY_ERR lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
-
-/**
- * @brief Validate value of the YANG built-in instance-identifier type.
- * Implementation of the ::lyplg_type_validate_clb.
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in instance-identifier type.
  */
 LY_ERR lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
         const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
 
 /**
- * @brief Free value of the YANG built-in instance-identifier types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in instance-identifier type.
+ */
+LY_ERR lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in instance-identifier type.
+ */
+const void *lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in instance-identifier type.
+ */
+LY_ERR lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in instance-identifier type.
  */
 void lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value);
 
@@ -761,16 +809,14 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in signed integer types.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in signed integer types.
  */
 LY_ERR lyplg_type_store_int(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Validate and canonize value of the YANG built-in unsigned integer types.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in unsigned integer types.
  */
 LY_ERR lyplg_type_store_uint(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -787,48 +833,41 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in leafref type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in leafref type.
  */
 LY_ERR lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Comparison callback checking the leafref value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in leafref type.
  */
 LY_ERR lyplg_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Printer callback printing the leafref value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in leafref type.
  */
 const void *lyplg_type_print_leafref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
- * @brief Hash key callback for a leafref value.
- * Implementation of ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for the built-in leafref type.
  */
 const void *lyplg_type_hash_leafref(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
 
 /**
- * @brief Duplication callback of the leafref values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in leafref type.
  */
 LY_ERR lyplg_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
 /**
- * @brief Validate value of the YANG built-in leafref type.
- * Implementation of the ::lyplg_type_validate_clb.
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in leafref type.
  */
 LY_ERR lyplg_type_validate_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
         const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
 
 /**
- * @brief Free value of the YANG built-in leafref types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in leafref type.
  */
 void lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value);
 
@@ -843,8 +882,7 @@
  */
 
 /**
- * @brief Validate and store value of the YANG built-in string type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in string type.
  */
 LY_ERR lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -861,48 +899,41 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the YANG built-in union type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in union type.
  */
 LY_ERR lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Comparison callback checking the union value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in union type.
  */
 LY_ERR lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Printer callback printing the union value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in union type.
  */
 const void *lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
- * @brief Hash key callback for a union value.
- * Implementation of ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for the built-in union type.
  */
 const void *lyplg_type_hash_union(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
 
 /**
- * @brief Duplication callback of the union values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in union type.
  */
 LY_ERR lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
 /**
- * @brief Validate value of the YANG built-in union type.
- * Implementation of the ::lyplg_type_validate_clb.
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in union type.
  */
 LY_ERR lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
         const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
 
 /**
- * @brief Free value of the YANG built-in union types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in union type.
  */
 void lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value);
 
@@ -917,35 +948,30 @@
  */
 
 /**
- * @brief Validate, canonize and store value of the ietf-yang-types xpath1.0 type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the ietf-yang-types xpath1.0 type.
  */
 LY_ERR lyplg_type_store_xpath10(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
         struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
 
 /**
- * @brief Comparison callback checking the xpath1.0 value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-yang-types xpath1.0 type.
  */
 LY_ERR lyplg_type_compare_xpath10(const struct lyd_value *val1, const struct lyd_value *val2);
 
 /**
- * @brief Printer callback printing the xpath1.0 value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the ietf-yang-types xpath1.0 type.
  */
 const void *lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len);
 
 /**
- * @brief Duplication callback of the xpath1.0 values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-yang-types xpath1.0 type.
  */
 LY_ERR lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
 
 /**
- * @brief Free value of the derived xpath1.0 types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-yang-types xpath1.0 type.
  */
 void lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value);
 
@@ -1005,7 +1031,7 @@
  * @return LY_SUCCESS if @p derived IS based on the @p base identity.
  * @return LY_ENOTFOUND if @p derived IS NOT not based on the @p base identity.
  */
-LY_ERR lyplg_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *derived);
+LY_ERR lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *derived);
 
 /**
  * @brief Data type validator for a range/length-restricted values.
@@ -1014,11 +1040,12 @@
  * @param[in] range Range (length) restriction information.
  * @param[in] value Value to check. In case of basetypes using unsigned integer values, the value is actually cast to uint64_t.
  * @param[in] strval String representation of the @p value for error logging.
+ * @param[in] strval_len Length of @p strval.
  * @param[out] err Error information in case of failure. The error structure can be freed by ::ly_err_free().
  * @return LY_ERR value according to the result of the validation.
  */
 LY_ERR lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
-        struct ly_err_item **err);
+        size_t strval_len, struct ly_err_item **err);
 
 /**
  * @brief Data type validator for pattern-restricted string values.
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
index 23f410b..195548d 100644
--- a/src/plugins_types/binary.c
+++ b/src/plugins_types/binary.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <ctype.h>
@@ -28,104 +26,376 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBinary binary (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | binary value size | yes | `void *` | value in binary |
+ */
+
+/**
+ * @brief base64 encode table
+ */
+static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * @brief Encode binary value into a base64 string value.
+ *
+ * Reference https://tools.ietf.org/html/rfc4648#section-4
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] data Binary data value.
+ * @param[in] size Size of @p data.
+ * @param[out] str Encoded base64 string.
+ * @param[out] str_len Length of returned @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_encode(const struct ly_ctx *ctx, const char *data, size_t size, char **str, size_t *str_len)
+{
+    uint32_t i;
+    char *ptr;
+
+    *str_len = (size + 2) / 3 * 4;
+    *str = malloc(*str_len + 1);
+    LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
+
+    ptr = *str;
+    for (i = 0; i < size - 2; i += 3) {
+        *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
+        *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
+        *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
+        *ptr++ = b64_etable[data[i + 2] & 0x3F];
+    }
+    if (i < size) {
+        *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
+        if (i == (size - 1)) {
+            *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
+            *ptr++ = '=';
+        } else {
+            *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
+            *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
+        }
+        *ptr++ = '=';
+    }
+    *ptr = '\0';
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief base64 decode table
+ */
+static const int b64_dtable[256] = {
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
+    56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,  0,  0,  1,  2,  3,  4,  5,  6,
+    7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  0,
+    0,  0,  0, 63,  0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * @brief Decode the binary value from a base64 string value.
+ *
+ * Reference https://tools.ietf.org/html/rfc4648#section-4
+ *
+ * @param[in] value Base64-encoded string value.
+ * @param[in] value_len Length of @p value.
+ * @param[out] data Decoded binary value.
+ * @param[out] size Size of @p data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_decode(const char *value, size_t value_len, void **data, size_t *size)
+{
+    unsigned char *ptr = (unsigned char *)value;
+    uint32_t pad_chars, octet_count;
+    char *str;
+
+    if (!value_len || (ptr[value_len - 1] != '=')) {
+        pad_chars = 0;
+    } else if (ptr[value_len - 2] == '=') {
+        pad_chars = 1;
+    } else {
+        pad_chars = 2;
+    }
+
+    octet_count = ((value_len + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
+    *size = octet_count / 4 * 3 + pad_chars;
+
+    str = malloc(*size + 1);
+    LY_CHECK_RET(!str, LY_EMEM);
+    str[*size] = '\0';
+
+    for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
+        int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
+        str[j++] = n >> 16;
+        str[j++] = n >> 8 & 0xFF;
+        str[j++] = n & 0xFF;
+    }
+    if (pad_chars) {
+        int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
+        str[*size - pad_chars] = n >> 16;
+
+        if (pad_chars == 2) {
+            n |= b64_dtable[ptr[octet_count + 2]] << 6;
+            n >>= 8 & 0xFF;
+            str[*size - pad_chars + 1] = n;
+        }
+    }
+
+    *data = str;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate a base64 string.
+ *
+ * @param[in] value Value to validate.
+ * @param[in] value_len Length of @p value.
+ * @param[in] type type of the value.
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_validate(const char *value, size_t value_len, const struct lysc_type_bin *type, struct ly_err_item **err)
+{
+    uint32_t idx, pad;
+
+    /* check correct characters in base64 */
+    idx = 0;
+    while ((idx < value_len) &&
+            ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
+            (('a' <= value[idx]) && (value[idx] <= 'z')) ||
+            (('0' <= value[idx]) && (value[idx] <= '9')) ||
+            ('+' == value[idx]) || ('/' == value[idx]))) {
+        idx++;
+    }
+
+    /* find end of padding */
+    pad = 0;
+    while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
+        pad++;
+    }
+
+    /* check if value is valid base64 value */
+    if (value_len != idx + pad) {
+        if (isprint(value[idx + pad])) {
+            return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
+        } else {
+            return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
+        }
+    }
+
+    if (value_len & 3) {
+        /* base64 length must be multiple of 4 chars */
+        return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
+    }
+
+    /* length restriction of the binary value */
+    if (type->length) {
+        const uint32_t octet_count = ((idx + pad) / 4) * 3 - pad;
+        LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type->length, octet_count, value, value_len, err));
+    }
+
+    return LY_SUCCESS;
+}
+
 API LY_ERR
 lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
         struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
-    size_t base64_start, base64_end, base64_count, base64_terminated, value_end;
-    const char *value_str = value;
     struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
+    struct lyd_value_binary *val;
 
-    LY_CHECK_ARG_RET(ctx, value, LY_EINVAL);
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    *err = NULL;
+    if (format == LY_VALUE_LYB) {
+        /* allocate the value */
+        val = calloc(1, sizeof *val);
+        LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->bin = val;
+        storage->realtype = type;
+
+        /* store value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            val->data = (void *)value;
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        } else {
+            val->data = malloc(value_len);
+            LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
+            memcpy(val->data, value, value_len);
+        }
+
+        /* store size */
+        val->size = value_len;
+
+        /* success */
+        goto cleanup;
+    }
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    LY_CHECK_GOTO(ret, cleanup);
 
-    /* validate characters and remember the number of octets for length validation */
-    /* silently skip leading whitespaces */
-    base64_start = 0;
-    while (base64_start < value_len && isspace(value_str[base64_start])) {
-        base64_start++;
-    }
-    /* silently skip trailing whitespace */
-    value_end = value_len;
-    while (base64_start < value_end && isspace(value_str[value_end - 1])) {
-        value_end--;
+    /* validate */
+    if (format != LY_VALUE_CANON) {
+        ret = binary_base64_validate(value, value_len, type_bin, err);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
-    /* find end of base64 value */
-    base64_end = base64_start;
-    base64_count = 0;
-    while ((base64_end < value_len) &&
-            /* check correct character in base64 */
-            ((('A' <= value_str[base64_end]) && (value_str[base64_end] <= 'Z')) ||
-            (('a' <= value_str[base64_end]) && (value_str[base64_end] <= 'z')) ||
-            (('0' <= value_str[base64_end]) && (value_str[base64_end] <= '9')) ||
-            ('+' == value_str[base64_end]) ||
-            ('/' == value_str[base64_end]) ||
-            ('\n' == value_str[base64_end]))) {
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
 
-        if ('\n' != value_str[base64_end]) {
-            base64_count++;
-        }
-        base64_end++;
-    }
-
-    /* find end of padding */
-    base64_terminated = 0;
-    while (((base64_end < value_len) && (base64_terminated < 2)) &&
-            /* check padding on end of string */
-            (('=' == value_str[base64_end]) ||
-            ('\n' == value_str[base64_end]))) {
-
-        if ('\n' != value_str[base64_end]) {
-            base64_terminated++;
-        }
-        base64_end++;
-    }
-
-    /* check if value is valid base64 value */
-    if (value_end != base64_end) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character (%c).", value_str[base64_end]);
-        goto cleanup;
-    }
-
-    if ((base64_count + base64_terminated) & 3) {
-        /* base64 length must be multiple of 4 chars */
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
-        goto cleanup;
-    }
-
-    /* check if value meets the type requirements */
-    if (type_bin->length) {
-        const uint32_t value_length = ((base64_count + base64_terminated) / 4) * 3 - base64_terminated;
-        ret = lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, value_length, value_str, err);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    }
-
-    if (base64_count != 0) {
-        ret = lydict_insert(ctx, &value_str[base64_start], base64_end - base64_start, &storage->_canonical);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    } else {
-        ret = lydict_insert(ctx, "", 0, &storage->_canonical);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    }
-    storage->ptr = NULL;
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->bin = val;
     storage->realtype = type;
 
+    /* get the binary value */
+    ret = binary_base64_decode(value, value_len, &val->data, &val->size);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* store canonical value, it always is */
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_binary(ctx, storage);
     }
     return ret;
 }
 
+API LY_ERR
+lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_binary *v1 = val1->bin, *v2 = val2->bin;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
+        return LY_ENOT;
+    }
+    return LY_SUCCESS;
+}
+
+API const void *
+lyplg_type_print_binary(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lyd_value_binary *val = value->bin;
+    char *ret;
+    size_t ret_len = 0;
+
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = val->size;
+        }
+        return val->data;
+    }
+
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* get the base64 string value */
+        if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
+            return NULL;
+        }
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = ret_len ? ret_len : strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+API const void *
+lyplg_type_hash_binary(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    struct lyd_value_binary *val = value->bin;
+
+    /* return the value itself */
+    *dynamic = 0;
+    *key_len = val->size;
+    return val->data;
+}
+
+API LY_ERR
+lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+    LY_ERR ret;
+    struct lyd_value_binary *orig_val = original->bin, *dup_val;
+
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_RET(ret);
+
+    dup_val = calloc(1, sizeof *dup_val);
+    if (!dup_val) {
+        lydict_remove(ctx, dup->_canonical);
+        return LY_EMEM;
+    }
+
+    dup_val->data = malloc(orig_val->size);
+    if (!dup_val->data) {
+        lydict_remove(ctx, dup->_canonical);
+        free(dup_val);
+        return LY_EMEM;
+    }
+    memcpy(dup_val->data, orig_val->data, orig_val->size);
+    dup_val->size = orig_val->size;
+
+    dup->bin = dup_val;
+    dup->realtype = original->realtype;
+    return LY_SUCCESS;
+}
+
+API void
+lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    struct lyd_value_binary *val = value->bin;
+
+    lydict_remove(ctx, value->_canonical);
+    if (val) {
+        free(val->data);
+        free(val);
+    }
+}
+
 /**
  * @brief Plugin information for binray type implementation.
  *
@@ -142,11 +412,11 @@
         .plugin.id = "libyang 2 - binary, version 1",
         .plugin.store = lyplg_type_store_binary,
         .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple,
+        .plugin.compare = lyplg_type_compare_binary,
+        .plugin.print = lyplg_type_print_binary,
+        .plugin.hash = lyplg_type_hash_binary,
+        .plugin.duplicate = lyplg_type_dup_binary,
+        .plugin.free = lyplg_type_free_binary,
     },
     {0}
 };
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
index a74ada4..7c48c85 100644
--- a/src/plugins_types/bits.c
+++ b/src/plugins_types/bits.c
@@ -12,7 +12,8 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
 
 #include "plugins_types.h"
 
@@ -29,197 +30,479 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBits bits (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | returned by ::lyplg_type_bits_bitmap_size() | yes | pointer to integer type of the specific size, if size more than 8 use `char *` | bitmap of the set bits |
+ */
+
+/**
+ * @brief Get the position of the last bit.
+ */
+#define BITS_LAST_BIT_POSITION(type_bits) (type_bits->bits[LY_ARRAY_COUNT(type_bits->bits) - 1].position)
+
+/**
+ * @brief Get a specific byte in a bitmap.
+ */
+#ifdef IS_BIG_ENDIAN
+# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + (size - 1) - idx)
+#else
+# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + idx)
+#endif
+
+API size_t
+lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type)
+{
+    size_t needed_bytes, size;
+
+    LY_CHECK_ARG_RET(NULL, type, type->basetype == LY_TYPE_BITS, 0);
+
+    /* minimum needed bytes to hold all the bit positions */
+    needed_bytes = (BITS_LAST_BIT_POSITION(type) / 8) + (BITS_LAST_BIT_POSITION(type) % 8 ? 1 : 0);
+    LY_CHECK_ERR_RET(!needed_bytes, LOGINT(NULL), 0);
+
+    if ((needed_bytes == 1) || (needed_bytes == 2)) {
+        /* uint8_t or uint16_t */
+        size = needed_bytes;
+    } else if (needed_bytes < 5) {
+        /* uint32_t */
+        size = 4;
+    } else if (needed_bytes < 9) {
+        /* uint64_t */
+        size = 8;
+    } else {
+        /* no basic type, do not round */
+        size = needed_bytes;
+    }
+
+    return size;
+}
+
+API ly_bool
+lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position)
+{
+    char bitmask;
+
+    /* find the byte with our bit */
+    (void)size;
+    bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8);
+    bit_position %= 8;
+
+    /* generate bitmask */
+    bitmask = 1;
+    bitmask <<= bit_position;
+
+    /* check if bit set */
+    if (*bitmap & bitmask) {
+        return 1;
+    }
+    return 0;
+}
+
+/**
+ * @brief Set bit at a specific position.
+ *
+ * @param[in,out] bitmap Bitmap to modify.
+ * @param[in] size Size of @p bitmap.
+ * @param[in] bit_position Bit position to set.
+ */
+static void
+bits_bit_set(char *bitmap, size_t size, uint32_t bit_position)
+{
+    char bitmask;
+
+    /* find the byte with our bit */
+    (void)size;
+    bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8);
+    bit_position %= 8;
+
+    /* generate bitmask */
+    bitmask = 1;
+    bitmask <<= bit_position;
+
+    /* set the bit */
+    *bitmap |= bitmask;
+}
+
+/**
+ * @brief Convert a list of bit names separated by whitespaces to a bitmap.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] type Type of the value.
+ * @param[in,out] bitmap Zeroed bitmap, is filled (set).
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+bits_str2bitmap(const char *value, size_t value_len, struct lysc_type_bits *type, char *bitmap, struct ly_err_item **err)
+{
+    size_t idx_start, idx_end;
+    LY_ARRAY_COUNT_TYPE u;
+    ly_bool found;
+
+    idx_start = idx_end = 0;
+    while (idx_end < value_len) {
+        /* skip whitespaces */
+        while ((idx_end < value_len) && isspace(value[idx_end])) {
+            ++idx_end;
+        }
+        if (idx_end == value_len) {
+            break;
+        }
+
+        /* parse bit name */
+        idx_start = idx_end;
+        while ((idx_end < value_len) && !isspace(value[idx_end])) {
+            ++idx_end;
+        }
+
+        /* find the bit */
+        found = 0;
+        LY_ARRAY_FOR(type->bits, u) {
+            if (!ly_strncmp(type->bits[u].name, value + idx_start, idx_end - idx_start)) {
+                found = 1;
+                break;
+            }
+        }
+
+        /* check if name exists */
+        if (!found) {
+            return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid bit \"%.*s\".", (int)(idx_end - idx_start),
+                    value + idx_start);
+        }
+
+        /* check for duplication */
+        if (lyplg_type_bits_is_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position)) {
+            return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Duplicate bit \"%s\".", type->bits[u].name);
+        }
+
+        /* set the bit */
+        bits_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position);
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Add a bit item into an array.
+ *
+ * @param[in] position Bit position to add.
+ * @param[in] type Bitis type to read the bit positions and names from.
+ * @param[in,out] items Array of bit item pointers to add to.
+ */
+static void
+bits_add_item(uint32_t position, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items)
+{
+    LY_ARRAY_COUNT_TYPE u;
+
+    /* find the bit item */
+    LY_ARRAY_FOR(type->bits, u) {
+        if (type->bits[u].position == position) {
+            break;
+        }
+    }
+
+    /* add it at the end */
+    items[LY_ARRAY_COUNT(items)] = &type->bits[u];
+    LY_ARRAY_INCREMENT(items);
+}
+
+/**
+ * @brief Convert a bitmap to a sized array of pointers to their bit definitions.
+ *
+ * @param[in] bitmap Bitmap to read from.
+ * @param[in] type Bits type.
+ * @param[in,out] items Allocated sized array to fill with the set bits.
+ */
+static void
+bits_bitmap2items(const char *bitmap, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items)
+{
+    size_t i, bitmap_size = lyplg_type_bits_bitmap_size(type);
+    uint32_t bit_pos;
+    char bitmask;
+    const char *byte;
+
+    bit_pos = 0;
+    for (i = 0; i < bitmap_size; ++i) {
+        /* check this byte (but not necessarily all bits in the last byte) */
+        byte = BITS_BITMAP_BYTE(bitmap, bitmap_size, i);
+        for (bitmask = 1; bitmask; bitmask <<= 1) {
+            if (*byte & bitmask) {
+                /* add this bit */
+                bits_add_item(bit_pos, type, items);
+            }
+
+            if (bit_pos == BITS_LAST_BIT_POSITION(type)) {
+                /* we have checked the last valid bit */
+                break;
+            }
+
+            ++bit_pos;
+        }
+    }
+}
+
+/**
+ * @brief Generate canonical value from ordered array of set bit items.
+ *
+ * @param[in] items Sized array of set bit items.
+ * @param[out] canonical Canonical string value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+bits_items2canon(struct lysc_type_bitenum_item **items, char **canonical)
+{
+    char *ret;
+    size_t ret_len;
+    LY_ARRAY_COUNT_TYPE u;
+
+    *canonical = NULL;
+
+    /* init value */
+    ret = strdup("");
+    LY_CHECK_RET(!ret, LY_EMEM);
+    ret_len = 0;
+
+    LY_ARRAY_FOR(items, u) {
+        if (!ret_len) {
+            ret = ly_realloc(ret, strlen(items[u]->name) + 1);
+            LY_CHECK_RET(!ret, LY_EMEM);
+            strcpy(ret, items[u]->name);
+
+            ret_len = strlen(ret);
+        } else {
+            ret = ly_realloc(ret, ret_len + 1 + strlen(items[u]->name) + 1);
+            LY_CHECK_RET(!ret, LY_EMEM);
+            sprintf(ret + ret_len, " %s", items[u]->name);
+
+            ret_len += 1 + strlen(items[u]->name);
+        }
+    }
+
+    *canonical = ret;
+    return LY_SUCCESS;
+}
+
 API LY_ERR
 lyplg_type_store_bits(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
         struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type;
-    struct lysc_type_bitenum_item **bits_items = NULL;
-    struct ly_set *items = NULL;
-    const char *value_str = value;
+    struct lyd_value_bits *val;
 
-    uint32_t index_start;   /* start index of bit name */
-    uint32_t index_end = 0; /* end index of bit name */
-    uint32_t item_len;      /* length of bit name */
-    uint32_t buf_len = 0;
-    uint32_t item_pos;
-    uint32_t item_pos_expected;
-    char *buf = NULL;
-    const char *item = NULL;
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    ly_bool item_present;
-    LY_ARRAY_COUNT_TYPE item_present_index;
-    LY_ARRAY_COUNT_TYPE it;
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != lyplg_type_bits_bitmap_size(type_bits)) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB bits value size %zu (expected %zu).",
+                    value_len, lyplg_type_bits_bitmap_size(type_bits));
+            goto cleanup;
+        }
 
-    *err = NULL;
+        /* allocate the value */
+        val = calloc(1, sizeof *val);
+        LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = val;
+        storage->realtype = type;
+
+        /* store value (bitmap) */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            val->bitmap = (char *)value;
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        } else {
+            val->bitmap = malloc(value_len);
+            LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
+            memcpy(val->bitmap, value, value_len);
+        }
+
+        /* allocate and fill the bit item array */
+        LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup);
+        bits_bitmap2items(val->bitmap, type_bits, val->items);
+
+        /* success */
+        goto cleanup;
+    }
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup_value);
+    LY_CHECK_GOTO(ret, cleanup);
 
-    /* remember the present items for further work */
-    ret = ly_set_new(&items);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup_value);
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
 
-    /* get all values */
-    while (index_end < value_len) {
-        /* skip leading spaces */
-        index_start = index_end;
-        while ((index_start < value_len) && isspace(value_str[index_start])) {
-            index_start++;
-        }
-
-        index_end = index_start;
-        /* find end of word */
-        while ((index_end < value_len) && !isspace(value_str[index_end])) {
-            index_end++;
-        }
-
-        /* check if name of bit is valid */
-        item = &value_str[index_start];
-        item_len = index_end - index_start;
-        if (item_len == 0) {
-            /* loop read all bits names*/
-            break;
-        }
-
-        /* looking for correct name */
-        item_present = 0;
-        LY_ARRAY_FOR(type_bits->bits, it) {
-            if (!ly_strncmp(type_bits->bits[it].name, item, item_len)) {
-                item_present = 1;
-                item_present_index = it;
-            }
-        }
-
-        /* check if name exists */
-        if (!item_present) {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
-                    "Invalid bit value \"%.*s\".", (int)item_len, item);
-            goto cleanup;
-        }
-
-        /* add item to set */
-        item_pos_expected = items->count;
-        ret = ly_set_add(items, &type_bits->bits[item_present_index], 0, &item_pos);
-        LY_CHECK_GOTO(ret, cleanup);
-        if (item_pos != item_pos_expected) {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
-                    "Bit \"%s\" used multiple times.", type_bits->bits[item_present_index].name);
-            goto cleanup;
-        }
-        /* count require buff size */
-        buf_len += item_len + 1;
-    }
-
-    /* creating buffer for cannonical value */
-    if (buf_len != 0) {
-        uint32_t buf_index = 0;
-        LY_ARRAY_COUNT_TYPE it;
-
-        /* create space for cannonical value and array for bits*/
-        buf = malloc(buf_len * sizeof *buf);
-        if (buf == NULL) {
-            ret = LY_EMEM;
-            LOGMEM(ctx);
-            goto cleanup;
-        }
-        LY_ARRAY_CREATE_GOTO(ctx, bits_items, items->count, ret, cleanup);
-
-        /* generate ordered bits list and cannonical value*/
-        LY_ARRAY_FOR(type_bits->bits, it) {
-            if (ly_set_contains(items, &type_bits->bits[it], NULL)) {
-                uint32_t name_index = 0;
-
-                /* write space */
-                if (buf_index != 0) {
-                    buf[buf_index] = ' ';
-                    buf_index++;
-                }
-
-                /* write bit name*/
-                while (type_bits->bits[it].name[name_index]) {
-                    buf[buf_index] = type_bits->bits[it].name[name_index];
-                    buf_index++;
-                    name_index++;
-                }
-
-                bits_items[LY_ARRAY_COUNT(bits_items)] = &type_bits->bits[it];
-                LY_ARRAY_INCREMENT(bits_items);
-            }
-        }
-        buf[buf_index] = 0;
-
-        ret = lydict_insert_zc(ctx, buf, &storage->_canonical);
-        buf = NULL;
-        LY_CHECK_GOTO(ret, cleanup);
-    } else {
-        bits_items = NULL;
-        ret = lydict_insert(ctx, "", 0, &storage->_canonical);
-        buf = NULL;
-        LY_CHECK_GOTO(ret, cleanup);
-    }
-
-    /* store value */
-    storage->bits_items = bits_items;
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = val;
     storage->realtype = type;
 
-    /* RETURN LY_SUCCESS */
-    ly_set_free(items, NULL);
+    /* allocate the bitmap */
+    val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
+    LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
+    memset(val->bitmap, 0, lyplg_type_bits_bitmap_size(type_bits));
+
+    /* fill the bitmap */
+    ret = bits_str2bitmap(value, value_len, type_bits, val->bitmap, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* allocate and fill the bit item array */
+    LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup);
+    bits_bitmap2items(val->bitmap, type_bits, val->items);
+
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+        }
+    }
+
+cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_bits(ctx, storage);
+    }
+    return ret;
+}
+
+API LY_ERR
+lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_bits *v1 = val1->ptr, *v2 = val2->ptr;
+    struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    if (memcmp(v1->bitmap, v2->bitmap, lyplg_type_bits_bitmap_size(type_bits))) {
+        return LY_ENOT;
     }
     return LY_SUCCESS;
+}
 
-    /* ERROR HANDLING */
-cleanup:
-    LY_ARRAY_FREE(bits_items);
-    free(buf);
-    ly_set_free(items, NULL);
-cleanup_value:
-    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+API const void *
+lyplg_type_print_bits(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lysc_type_bits *type_bits = (struct lysc_type_bits *)value->realtype;
+    struct lyd_value_bits *val = value->ptr;
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = lyplg_type_bits_bitmap_size(type_bits);
+        }
+        return val->bitmap;
     }
 
-    return ret;
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* get the canonical value */
+        if (bits_items2canon(val->items, &ret)) {
+            return NULL;
+        }
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+API const void *
+lyplg_type_hash_bits(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    struct lysc_type_bits *type_bits = (struct lysc_type_bits *)value->realtype;
+    struct lyd_value_bits *val = value->ptr;
+
+    /* return the bitmap */
+    *dynamic = 0;
+    *key_len = lyplg_type_bits_bitmap_size(type_bits);
+    return val->bitmap;
 }
 
 API LY_ERR
 lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR ret;
+    struct lysc_type_bits *type_bits = (struct lysc_type_bits *)original->realtype;
     LY_ARRAY_COUNT_TYPE u;
-    struct lysc_type_bitenum_item **bits_items = NULL;
+    struct lyd_value_bits *orig_val = original->ptr, *dup_val;
 
-    LY_ARRAY_CREATE_RET(ctx, bits_items, LY_ARRAY_COUNT(original->bits_items), LY_EMEM);
-    LY_ARRAY_FOR(original->bits_items, u) {
-        LY_ARRAY_INCREMENT(bits_items);
-        bits_items[u] = original->bits_items[u];
+    memset(dup, 0, sizeof *dup);
+
+    /* optional canonical value */
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_GOTO(ret, error);
+
+    /* allocate value */
+    dup_val = calloc(1, sizeof *dup_val);
+    LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+    dup->ptr = dup_val;
+
+    /* duplicate bitmap */
+    dup_val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
+    LY_CHECK_ERR_GOTO(!dup_val->bitmap, ret = LY_EMEM, error);
+    memcpy(dup_val->bitmap, orig_val->bitmap, lyplg_type_bits_bitmap_size(type_bits));
+
+    /* duplicate bit item pointers */
+    LY_ARRAY_CREATE_GOTO(ctx, dup_val->items, LY_ARRAY_COUNT(orig_val->items), ret, error);
+    LY_ARRAY_FOR(orig_val->items, u) {
+        LY_ARRAY_INCREMENT(dup_val->items);
+        dup_val->items[u] = orig_val->items[u];
     }
 
-    ret = lydict_insert(ctx, original->_canonical, strlen(original->_canonical), &dup->_canonical);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    dup->bits_items = bits_items;
+    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 
-    /* ERROR HANDLING */
-cleanup:
-    LY_ARRAY_FREE(bits_items);
+error:
+    lyplg_type_free_bits(ctx, dup);
     return ret;
 }
 
 API void
 lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    LY_ARRAY_FREE(value->bits_items);
-    value->bits_items = NULL;
+    struct lyd_value_bits *val = value->ptr;
 
     lydict_remove(ctx, value->_canonical);
-    value->_canonical = NULL;
+    if (val) {
+        free(val->bitmap);
+        LY_ARRAY_FREE(val->items);
+        free(val);
+    }
 }
 
 /**
@@ -238,9 +521,9 @@
         .plugin.id = "libyang 2 - bits, version 1",
         .plugin.store = lyplg_type_store_bits,
         .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
+        .plugin.compare = lyplg_type_compare_bits,
+        .plugin.print = lyplg_type_print_bits,
+        .plugin.hash = lyplg_type_hash_bits,
         .plugin.duplicate = lyplg_type_dup_bits,
         .plugin.free = lyplg_type_free_bits
     },
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
index 5a681ad..abb27d4 100644
--- a/src/plugins_types/boolean.c
+++ b/src/plugins_types/boolean.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <stdint.h>
@@ -28,21 +26,53 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBoolean boolean (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 1 | yes | `int8_t *` | 0 for false, otherwise true |
+ */
+
 API LY_ERR
 lyplg_type_store_boolean(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
         struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
     int8_t i;
 
-    *err = NULL;
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 1) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB boolean value size %zu (expected 1).",
+                    value_len);
+            goto cleanup;
+        }
+
+        /* cast the value */
+        i = *(int8_t *)value;
+
+        /* init storage and set the value */
+        storage->_canonical = NULL;
+        storage->boolean = i ? 1 : 0;
+        storage->realtype = type;
+
+        /* store canonical value */
+        ret = lydict_insert(ctx, i ? "true" : "false", 0, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* success */
+        goto cleanup;
+    }
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    LY_CHECK_GOTO(ret, cleanup);
 
+    /* validate and get the value */
     if ((value_len == ly_strlen_const("true")) && !strncmp(value, "true", ly_strlen_const("true"))) {
         i = 1;
     } else if ((value_len == ly_strlen_const("false")) && !strncmp(value, "false", ly_strlen_const("false"))) {
@@ -53,21 +83,29 @@
         goto cleanup;
     }
 
+    /* init storage and set the value */
+    storage->_canonical = NULL;
+    storage->boolean = i;
+    storage->realtype = type;
+
+    /* store canonical value, it always is */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
         ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
         options &= ~LYPLG_TYPE_STORE_DYNAMIC;
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+        LY_CHECK_GOTO(ret, cleanup);
     } else {
         ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+        LY_CHECK_GOTO(ret, cleanup);
     }
-    storage->boolean = i;
-    storage->realtype = type;
 
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
         free((char *)value);
     }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
+    }
     return ret;
 }
 
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 24b5034..ec8a25f 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -12,16 +12,19 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
 
 #include "plugins_types.h"
 
 #include <arpa/inet.h>
 #include <assert.h>
+#include <ctype.h>
 #include <errno.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 
 #include "libyang.h"
 
@@ -29,228 +32,404 @@
 #include "compat.h"
 
 /**
- * @brief Convert string to a number.
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDateAndTime date-and-time (ietf-yang-types)
  *
- * @param[in,out] str String to convert, the parsed number are skipped.
- * @param[in] len Expected length of the number. 0 to parse at least 1 character.
- * @param[in] full_str Full string for error generation.
- * @param[out] num Converted number.
- * @param[out] err Error information on error.
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 8        | yes | `time_t *` | UNIX timestamp |
+ * | string length | no | `char *` | string with the fraction digits of a second |
+ */
+
+static void lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Stored value structure for date-and-time
+ */
+struct lyd_value_date_and_time {
+    time_t time;        /**< UNIX timestamp */
+    char *fractions_s;  /**< fractions of a second */
+};
+
+/**
+ * @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
+ *
+ * @param[in] value Valid string value.
+ * @param[out] time UNIX timestamp.
+ * @param[out] fractions_s Fractions of a second, set to NULL if none.
  * @return LY_ERR value.
  */
 static LY_ERR
-convert_number(const char **str, int len, const char *full_str, int *num, struct ly_err_item **err)
+dat_str2time(const char *value, time_t *time, char **fractions_s)
 {
-    char *ptr;
+    struct tm tm = {0};
+    uint32_t i, frac_len;
+    const char *frac;
+    int64_t shift, shift_m;
+    time_t t;
 
-    *num = strtoul(*str, &ptr, 10);
-    if ((len && (ptr - *str != len)) || (!len && (ptr == *str))) {
-        return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
-                "a digit expected.", ptr[0], full_str);
+    tm.tm_year = atoi(&value[0]) - 1900;
+    tm.tm_mon = atoi(&value[5]) - 1;
+    tm.tm_mday = atoi(&value[8]);
+    tm.tm_hour = atoi(&value[11]);
+    tm.tm_min = atoi(&value[14]);
+    tm.tm_sec = atoi(&value[17]);
+
+    t = timegm(&tm);
+    i = 19;
+
+    /* fractions of a second */
+    if (value[i] == '.') {
+        ++i;
+        frac = &value[i];
+        for (frac_len = 0; isdigit(frac[frac_len]); ++frac_len) {}
+
+        i += frac_len;
+
+        /* skip trailing zeros */
+        for ( ; frac_len && (frac[frac_len - 1] == '0'); --frac_len) {}
+
+        if (!frac_len) {
+            /* only zeros, ignore */
+            frac = NULL;
+        }
+    } else {
+        frac = NULL;
     }
 
-    *str = ptr;
+    /* apply offset */
+    if ((value[i] == 'Z') || (value[i] == 'z')) {
+        /* zero shift */
+        shift = 0;
+    } else {
+        shift = strtol(&value[i], NULL, 10);
+        shift = shift * 60 * 60; /* convert from hours to seconds */
+        shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
+        /* correct sign */
+        if (shift < 0) {
+            shift_m *= -1;
+        }
+        /* connect hours and minutes of the shift */
+        shift = shift + shift_m;
+    }
+
+    /* we have to shift to the opposite way to correct the time */
+    t -= shift;
+
+    *time = t;
+    if (frac) {
+        *fractions_s = strndup(frac, frac_len);
+        LY_CHECK_RET(!*fractions_s, LY_EMEM);
+    } else {
+        *fractions_s = NULL;
+    }
     return LY_SUCCESS;
 }
 
 /**
- * @brief Check a single character.
+ * @brief Convert UNIX timestamp and fractions of a second into canonical date-and-time string value.
  *
- * @param[in,out] str String to check, the parsed character is skipped.
- * @param[in] c Array of possible characters.
- * @param[in] full_str Full string for error generation.
- * @param[out] err Error information on error.
+ * @param[in] time UNIX timestamp.
+ * @param[in] fractions_s Fractions of a second, if any.
+ * @param[out] str Canonical string value.
  * @return LY_ERR value.
  */
 static LY_ERR
-check_char(const char **str, char c[], const char *full_str, struct ly_err_item **err)
+dat_time2str(time_t time, const char *fractions_s, char **str)
 {
-    LY_ERR ret;
-    uint32_t i;
-    char *exp_str;
+    struct tm tm;
+    char zoneshift[7];
+    int32_t zonediff_h, zonediff_m;
 
-    for (i = 0; c[i]; ++i) {
-        if ((*str)[0] == c[i]) {
-            break;
-        }
-    }
-    if (!c[i]) {
-        /* "'c'" + (" or 'c'")* + \0 */
-        exp_str = malloc(3 + (i - 1) * 7 + 1);
-        sprintf(exp_str, "'%c'", c[0]);
-        for (i = 1; c[i]; ++i) {
-            sprintf(exp_str + strlen(exp_str), " or '%c'", c[i]);
-        }
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
-                "%s expected.", (*str)[0], full_str, exp_str);
-        free(exp_str);
-        return ret;
+    /* initialize the local timezone */
+    tzset();
+
+    /* convert */
+    if (!localtime_r(&time, &tm)) {
+        return LY_ESYS;
     }
 
-    ++(*str);
+    /* get timezone offset */
+    if (tm.tm_gmtoff == 0) {
+        /* time is Zulu (UTC) */
+        zonediff_h = 0;
+        zonediff_m = 0;
+    } else {
+        /* timezone offset */
+        zonediff_h = tm.tm_gmtoff / 60 / 60;
+        zonediff_m = tm.tm_gmtoff / 60 % 60;
+    }
+    sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
+
+    /* print */
+    if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
+            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+            fractions_s ? "." : "", fractions_s ? fractions_s : "", zoneshift) == -1) {
+        return LY_EMEM;
+    }
+
     return LY_SUCCESS;
 }
 
 /**
- * @brief Validate, canonize and store value of the ietf-yang-types date-and-time type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for ietf-yang-types date-and-time type.
  */
 static LY_ERR
 lyplg_type_store_date_and_time(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
-        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
-    struct tm tm = {0}, tm2;
-    int num;
-    const char *val_str;
+    struct lysc_type_str *type_dat = (struct lysc_type_str *)type;
+    struct lyd_value_date_and_time *val;
+    uint32_t i;
+    char c;
 
-    /* store as a string */
-    ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
-            storage, unres, err);
-    LY_CHECK_RET(ret);
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    /* canonize */
-    /* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})
-     * 2018-03-21T09:11:05(.55785...)(Z|+02:00) */
-    val_str = storage->_canonical;
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len < 8) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time value size %zu "
+                    "(expected at least 8).", value_len);
+            goto cleanup;
+        }
+        for (i = 8; i < value_len; ++i) {
+            c = ((char *)value)[i];
+            if (!isdigit(c)) {
+                ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time character '%c' "
+                        "(expected a digit).", c);
+                goto cleanup;
+            }
+        }
 
-    /* year */
-    ret = convert_number(&val_str, 4, storage->_canonical, &tm.tm_year, err);
-    LY_CHECK_GOTO(ret, cleanup);
-    tm.tm_year -= 1900;
+        /* allocate the value */
+        val = calloc(1, sizeof *val);
+        LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
 
-    LY_CHECK_GOTO(ret = check_char(&val_str, "-", storage->_canonical, err), cleanup);
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = val;
+        storage->realtype = type;
 
-    /* month */
-    ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_mon, err);
-    LY_CHECK_GOTO(ret, cleanup);
-    tm.tm_mon -= 1;
+        /* store timestamp */
+        memcpy(&val->time, value, sizeof val->time);
 
-    LY_CHECK_GOTO(ret = check_char(&val_str, "-", storage->_canonical, err), cleanup);
+        /* store fractions of second */
+        if (value_len > 8) {
+            val->fractions_s = strndup(((char *)value) + 8, value_len - 8);
+            LY_CHECK_ERR_GOTO(!val->fractions_s, ret = LY_EMEM, cleanup);
+        }
 
-    /* day */
-    ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_mday, err);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    LY_CHECK_GOTO(ret = check_char(&val_str, "T", storage->_canonical, err), cleanup);
-
-    /* hours */
-    ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_hour, err);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    LY_CHECK_GOTO(ret = check_char(&val_str, ":", storage->_canonical, err), cleanup);
-
-    /* minutes */
-    ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_min, err);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    LY_CHECK_GOTO(ret = check_char(&val_str, ":", storage->_canonical, err), cleanup);
-
-    /* seconds */
-    ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_sec, err);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* do not move the pointer */
-    LY_CHECK_GOTO(ret = check_char(&val_str, ".Z+-", storage->_canonical, err), cleanup);
-    --val_str;
-
-    /* validate using mktime() */
-    tm2 = tm;
-    errno = 0;
-    mktime(&tm);
-    /* ENOENT is set when "/etc/localtime" is missing but the function suceeeds */
-    if (errno && (errno != ENOENT)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed (%s).",
-                storage->_canonical, strerror(errno));
-        goto cleanup;
-    }
-    /* we now have correctly filled the remaining values, use them */
-    memcpy(((char *)&tm2) + (6 * sizeof(int)), ((char *)&tm) + (6 * sizeof(int)), sizeof(struct tm) - (6 * sizeof(int)));
-    /* back it up again */
-    tm = tm2;
-    /* let mktime() correct date & time with having the other values correct now */
-    errno = 0;
-    mktime(&tm);
-    if (errno && (errno != ENOENT)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed (%s).",
-                storage->_canonical, strerror(errno));
-        goto cleanup;
-    }
-    /* detect changes in the filled values */
-    if (memcmp(&tm, &tm2, 6 * sizeof(int))) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed, "
-                "canonical date and time is \"%04d-%02d-%02dT%02d:%02d:%02d\".", storage->_canonical,
-                tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+        /* success */
         goto cleanup;
     }
 
-    /* tenth of a second */
-    if (val_str[0] == '.') {
-        ++val_str;
-        ret = convert_number(&val_str, 0, storage->_canonical, &num, err);
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction, there can be only ASCII chars */
+    if (type_dat->length) {
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_dat->length, value_len, value, value_len, err);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
-    switch (val_str[0]) {
-    case 'Z':
-        /* done */
-        ++val_str;
-        break;
-    case '+':
-    case '-':
-        /* timezone shift */
-        if ((val_str[1] < '0') || (val_str[1] > '2')) {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
-                    val_str, storage->_canonical);
-            goto cleanup;
-        }
-        if ((val_str[2] < '0') || ((val_str[1] == '2') && (val_str[2] > '3')) || (val_str[2] > '9')) {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
-                    val_str, storage->_canonical);
-            goto cleanup;
-        }
+    /* date-and-time pattern */
+    ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
 
-        if (val_str[3] != ':') {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
-                    val_str, storage->_canonical);
-            goto cleanup;
-        }
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
 
-        if ((val_str[4] < '0') || (val_str[4] > '5')) {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
-                    val_str, storage->_canonical);
-            goto cleanup;
-        }
-        if ((val_str[5] < '0') || (val_str[5] > '9')) {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
-                    val_str, storage->_canonical);
-            goto cleanup;
-        }
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = val;
+    storage->realtype = type;
 
-        val_str += 6;
-        break;
-    default:
-        LY_CHECK_GOTO(ret = check_char(&val_str, "Z+-", storage->_canonical, err), cleanup);
+    /* pattern validation succeeded, convert to UNIX time and fractions of second */
+    ret = dat_str2time(value, &val->time, &val->fractions_s);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
     }
 
-    /* no other characters expected */
-    if (val_str[0]) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
-                "no characters expected.", val_str[0], storage->_canonical);
-        goto cleanup;
-    }
-
-    /* validation succeeded and we do not want to change how it is stored */
-
 cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
     if (ret) {
-        type->plugin->free(ctx, storage);
+        lyplg_type_free_date_and_time(ctx, storage);
     }
     return ret;
 }
 
 /**
+ * @brief Implementation of ::lyplg_type_compare_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_compare_date_and_time(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_date_and_time *v1 = val1->ptr, *v2 = val2->ptr;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    /* compare timestamp */
+    if (v1->time != v2->time) {
+        return LY_ENOT;
+    }
+
+    /* compare second fractions */
+    if ((!v1->fractions_s && !v2->fractions_s) ||
+            (v1->fractions_s && v2->fractions_s && !strcmp(v1->fractions_s, v2->fractions_s))) {
+        return LY_SUCCESS;
+    }
+    return LY_ENOT;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_print_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lyd_value_date_and_time *val = value->ptr;
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        if (val->fractions_s) {
+            ret = malloc(8 + strlen(val->fractions_s));
+            LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
+
+            *dynamic = 1;
+            if (value_len) {
+                *value_len = 8 + strlen(val->fractions_s);
+            }
+            memcpy(ret, &val->time, sizeof val->time);
+            memcpy(ret + 8, val->fractions_s, strlen(val->fractions_s));
+        } else {
+            *dynamic = 0;
+            if (value_len) {
+                *value_len = 8;
+            }
+            ret = (char *)&val->time;
+        }
+        return ret;
+    }
+
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* get the canonical value */
+        if (dat_time2str(val->time, val->fractions_s, &ret)) {
+            return NULL;
+        }
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_hash_date_and_time(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    struct lyd_value_date_and_time *val = value->ptr;
+
+    if (!val->fractions_s) {
+        /* we can use the timestamp */
+        *dynamic = 0;
+        *key_len = 8;
+        return &val->time;
+    }
+
+    /* simply use the (dynamic) LYB value */
+    return lyplg_type_print_date_and_time(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_dup_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+    LY_ERR ret;
+    struct lyd_value_date_and_time *orig_val = original->ptr, *dup_val;
+
+    memset(dup, 0, sizeof *dup);
+
+    /* optional canonical value */
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_GOTO(ret, error);
+
+    /* allocate value */
+    dup_val = calloc(1, sizeof *dup_val);
+    LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+    dup->ptr = dup_val;
+
+    /* copy timestamp */
+    dup_val->time = orig_val->time;
+
+    /* duplicate second fractions */
+    if (orig_val->fractions_s) {
+        dup_val->fractions_s = strdup(orig_val->fractions_s);
+        LY_CHECK_ERR_GOTO(!dup_val->fractions_s, ret = LY_EMEM, error);
+    }
+
+    dup->ptr = dup_val;
+    dup->realtype = original->realtype;
+    return LY_SUCCESS;
+
+error:
+    lyplg_type_free_date_and_time(ctx, dup);
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for ietf-yang-types date-and-time type.
+ */
+static void
+lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    struct lyd_value_date_and_time *val = value->ptr;
+
+    lydict_remove(ctx, value->_canonical);
+    if (val) {
+        free(val->fractions_s);
+        free(val);
+    }
+}
+
+/**
  * @brief Plugin information for date-and-time type implementation.
  *
  * Note that external plugins are supposed to use:
@@ -266,11 +445,11 @@
         .plugin.id = "libyang 2 - date-and-time, version 1",
         .plugin.store = lyplg_type_store_date_and_time,
         .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
+        .plugin.compare = lyplg_type_compare_date_and_time,
+        .plugin.print = lyplg_type_print_date_and_time,
+        .plugin.hash = lyplg_type_hash_date_and_time,
+        .plugin.duplicate = lyplg_type_dup_date_and_time,
+        .plugin.free = lyplg_type_free_date_and_time
     },
     {0}
 };
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
index 54416a5..2b7ab17 100644
--- a/src/plugins_types/decimal64.c
+++ b/src/plugins_types/decimal64.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <inttypes.h>
@@ -29,71 +27,135 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
-API LY_ERR
-lyplg_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
-        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
-        struct ly_err_item **err)
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDecimal64 decimal64 (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 8        | yes | `int64_t *` | value represented without floating point |
+ */
+
+/**
+ * @brief Convert decimal64 number to canonical string.
+ *
+ * @param[in] num Decimal64 number stored in int64.
+ * @param[in] type Decimal64 type with fraction digits.
+ * @param[out] str Canonical string value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+decimal64_num2str(int64_t num, struct lysc_type_dec *type, char **str)
 {
-    LY_ERR ret = LY_SUCCESS;
-    int64_t d;
-    struct lysc_type_dec *type_dec = (struct lysc_type_dec *)type;
-    char buf[LY_NUMBER_MAXLEN];
+    char *ret;
 
-    *err = NULL;
+    /* allocate the value */
+    ret = calloc(1, LY_NUMBER_MAXLEN);
+    LY_CHECK_RET(!ret, LY_EMEM);
 
-    if (!value || !((char *)value)[0] || !value_len) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL,  NULL, "Invalid empty decimal64 value.");
-        goto cleanup;
-    }
-
-    /* check hints */
-    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-
-    ret = lyplg_type_parse_dec64(type_dec->fraction_digits, value, value_len, &d, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    /* prepare canonized value */
-    if (d) {
-        int count = sprintf(buf, "%" PRId64 " ", d);
-        if (((d > 0) && ((count - 1) <= type_dec->fraction_digits)) ||
-                ((count - 2) <= type_dec->fraction_digits)) {
+    if (num) {
+        int count = sprintf(ret, "%" PRId64 " ", num);
+        if (((num > 0) && ((count - 1) <= type->fraction_digits)) || ((count - 2) <= type->fraction_digits)) {
             /* we have 0. value, print the value with the leading zeros
              * (one for 0. and also keep the correct with of num according
              * to fraction-digits value)
-             * for (num<0) - extra character for '-' sign */
-            count = sprintf(buf, "%0*" PRId64 " ", (d > 0) ? (type_dec->fraction_digits + 1) : (type_dec->fraction_digits + 2), d);
+             * for (num < 0) - extra character for '-' sign */
+            count = sprintf(ret, "%0*" PRId64 " ", (num > 0) ? (type->fraction_digits + 1) : (type->fraction_digits + 2), num);
         }
-        for (uint8_t i = type_dec->fraction_digits, j = 1; i > 0; i--) {
-            if (j && (i > 1) && (buf[count - 2] == '0')) {
+        for (uint8_t i = type->fraction_digits, j = 1; i > 0; i--) {
+            if (j && (i > 1) && (ret[count - 2] == '0')) {
                 /* we have trailing zero to skip */
-                buf[count - 1] = '\0';
+                ret[count - 1] = '\0';
             } else {
                 j = 0;
-                buf[count - 1] = buf[count - 2];
+                ret[count - 1] = ret[count - 2];
             }
             count--;
         }
-        buf[count - 1] = '.';
+        ret[count - 1] = '.';
     } else {
         /* zero */
-        sprintf(buf, "0.0");
+        sprintf(ret, "0.0");
     }
 
-    /* range of the number */
-    if (type_dec->range) {
-        ret = lyplg_type_validate_range(type->basetype, type_dec->range, d, buf, err);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    *str = ret;
+    return LY_SUCCESS;
+}
+
+API LY_ERR
+lyplg_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    struct lysc_type_dec *type_dec = (struct lysc_type_dec *)type;
+    LY_ERR ret = LY_SUCCESS;
+    int64_t num;
+    char *canon;
+
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 8) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB decimal64 value size %zu (expected 8).",
+                    value_len);
+            goto cleanup;
+        }
+
+        /* we have the decimal64 number */
+        num = *(int64_t *)value;
+    } else {
+        /* check hints */
+        ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse decimal64 value */
+        ret = lyplg_type_parse_dec64(type_dec->fraction_digits, value, value_len, &num, err);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
-    ret = lydict_insert(ctx, buf, strlen(buf), &storage->_canonical);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    storage->dec64 = d;
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->dec64 = num;
     storage->realtype = type;
 
+    /* we need canonical value for hash */
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else {
+        /* generate canonical value */
+        ret = decimal64_num2str(num, type_dec, &canon);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* store it */
+        ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    if (type_dec->range) {
+        /* check range of the number */
+        ret = lyplg_type_validate_range(type->basetype, type_dec->range, num, storage->_canonical,
+                strlen(storage->_canonical), err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
     }
     return ret;
 }
diff --git a/src/plugins_types/empty.c b/src/plugins_types/empty.c
index 915ef72..eb2306d 100644
--- a/src/plugins_types/empty.c
+++ b/src/plugins_types/empty.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <stdint.h>
@@ -27,6 +25,15 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEmpty empty (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 0        | yes | `void` | none |
+ */
+
 API LY_ERR
 lyplg_type_store_empty(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
@@ -35,32 +42,41 @@
 {
     LY_ERR ret = LY_SUCCESS;
 
-    *err = NULL;
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    LY_CHECK_GOTO(ret, cleanup);
 
+    /* validation */
     if (value_len) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty value \"%.*s\".", (int)value_len,
-                (char *)value);
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty value length %zu.", value_len);
         goto cleanup;
     }
 
-    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
-        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    } else {
-        ret = lydict_insert(ctx, "", value_len, &storage->_canonical);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    }
+    /* init storage */
+    storage->_canonical = NULL;
     storage->ptr = NULL;
     storage->realtype = type;
 
+    /* store canonical value */
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        ret = lydict_insert(ctx, "", value_len, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
     }
     return ret;
 }
diff --git a/src/plugins_types/enumeration.c b/src/plugins_types/enumeration.c
index dadf51f..22422f4 100644
--- a/src/plugins_types/enumeration.c
+++ b/src/plugins_types/enumeration.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <stdint.h>
@@ -27,56 +25,120 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEnumeration enumeration (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 4        | yes | `int32 *` | assigned value of the enum |
+ */
+
 API LY_ERR
 lyplg_type_store_enum(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
         struct ly_err_item **err)
 {
+    struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
     LY_ERR ret = LY_SUCCESS;
     LY_ARRAY_COUNT_TYPE u;
-    struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
+    ly_bool found = 0;
 
-    *err = NULL;
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    /* check hints */
-    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 4) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB enumeration value size %zu (expected 4).",
+                    value_len);
+            goto cleanup;
+        }
 
-    /* find the matching enumeration value item */
-    LY_ARRAY_FOR(type_enum->enums, u) {
-        if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
-            /* we have a match */
-            goto match;
+        /* find the matching enumeration value item */
+        LY_ARRAY_FOR(type_enum->enums, u) {
+            if (type_enum->enums[u].value == *(int32_t *)value) {
+                found = 1;
+                break;
+            }
+        }
+
+        if (!found) {
+            /* value not found */
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value % " PRIi32 ".",
+                    *(int32_t *)value);
+            goto cleanup;
+        }
+    } else {
+        /* check hints */
+        ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* find the matching enumeration value item */
+        LY_ARRAY_FOR(type_enum->enums, u) {
+            if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
+                found = 1;
+                break;
+            }
+        }
+
+        if (!found) {
+            /* enum not found */
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value \"%.*s\".", (int)value_len,
+                    (char *)value);
+            goto cleanup;
         }
     }
-    /* enum not found */
-    ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value \"%.*s\".", (int)value_len,
-            (char *)value);
-    goto cleanup;
 
-match:
-    /* validation done */
-
-    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
-        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    } else {
-        ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    }
+    /* init storage */
+    storage->_canonical = NULL;
     storage->enum_item = &type_enum->enums[u];
     storage->realtype = type;
 
-cleanup:
+    /* store canonical value */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *) value);
+        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
+    }
     return ret;
 }
 
+API const void *
+lyplg_type_print_enum(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = 4;
+        }
+        return &value->enum_item->value;
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
 /**
  * @brief Plugin information for enumeration type implementation.
  *
@@ -94,7 +156,7 @@
         .plugin.store = lyplg_type_store_enum,
         .plugin.validate = NULL,
         .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
+        .plugin.print = lyplg_type_print_enum,
         .plugin.hash = lyplg_type_hash_simple,
         .plugin.duplicate = lyplg_type_dup_simple,
         .plugin.free = lyplg_type_free_simple
diff --git a/src/plugins_types/hex_string.c b/src/plugins_types/hex_string.c
deleted file mode 100644
index ab8f2fa..0000000
--- a/src/plugins_types/hex_string.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * @file hex_string.c
- * @author Michal Vasko <mvasko@cesnet.cz>
- * @brief ietf-yang-types hex-string (and similar) type plugin.
- *
- * Copyright (c) 2019-2021 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
- */
-
-#define _GNU_SOURCE
-
-#include "plugins_types.h"
-
-#include <arpa/inet.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libyang.h"
-
-#include "common.h"
-#include "compat.h"
-
-/**
- * @brief Validate, canonize and store value of the ietf-yang-types hex-string type.
- * Implementation of the ::lyplg_type_store_clb.
- */
-static LY_ERR
-lyplg_type_store_hex_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
-        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
-{
-    LY_ERR ret = LY_SUCCESS;
-    char *str;
-    uint32_t i, len;
-
-    /* store as a string */
-    ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
-            storage, unres, err);
-    LY_CHECK_RET(ret);
-
-    str = strdup(storage->_canonical);
-    if (!str) {
-        /* we can hardly allocate an error message */
-        ret = LY_EMEM;
-        goto cleanup;
-    }
-
-    len = strlen(str);
-    for (i = 0; i < len; ++i) {
-        if ((str[i] >= 'A') && (str[i] <= 'Z')) {
-            /* make it lowercase (canonical format) */
-            str[i] += 32;
-        }
-    }
-
-    /* update the value correctly */
-    lydict_remove(ctx, storage->_canonical);
-    storage->_canonical = NULL;
-    LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, str, &storage->_canonical), cleanup);
-
-    /* validation succeeded and we do not want to change how it is stored */
-
-cleanup:
-    if (ret) {
-        type->plugin->free(ctx, storage);
-    }
-    return ret;
-}
-
-/**
- * @brief Plugin information for hex-string type implementation.
- *
- * Note that external plugins are supposed to use:
- *
- *   LYPLG_TYPES = {
- */
-const struct lyplg_type_record plugins_hex_string[] = {
-    {
-        .module = "ietf-yang-types",
-        .revision = "2013-07-15",
-        .name = "phys-address",
-
-        .plugin.id = "libyang 2 - phys-address, version 1",
-        .plugin.store = lyplg_type_store_hex_string,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
-    },
-    {
-        .module = "ietf-yang-types",
-        .revision = "2013-07-15",
-        .name = "mac-address",
-
-        .plugin.id = "libyang 2 - mac-address, version 1",
-        .plugin.store = lyplg_type_store_hex_string,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
-    },
-    {
-        .module = "ietf-yang-types",
-        .revision = "2013-07-15",
-        .name = "hex-string",
-
-        .plugin.id = "libyang 2 - hex-string, version 1",
-        .plugin.store = lyplg_type_store_hex_string,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
-    },
-    {
-        .module = "ietf-yang-types",
-        .revision = "2013-07-15",
-        .name = "uuid",
-
-        .plugin.id = "libyang 2 - uuid, version 1",
-        .plugin.store = lyplg_type_store_hex_string,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
-    },
-    {0}
-};
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 092d73a..a011ff6 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -12,7 +12,8 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
 
 #include "plugins_types.h"
 
@@ -29,101 +30,138 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
-static char *
-identityref_print(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data)
-{
-    char *result = NULL;
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIdentityref identityref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the identityref |
+ */
 
-    if (asprintf(&result, "%s:%s", lyplg_type_get_prefix(value->ident->module, format, prefix_data),
-            value->ident->name) == -1) {
-        return NULL;
-    } else {
-        return result;
+/**
+ * @brief Print an identityref value in a specific format.
+ *
+ * @param[in] ident Identityref to print.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed identityref.
+ * @param[out] str_len Optional length of @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_ident2str(const struct lysc_ident *ident, LY_VALUE_FORMAT format, void *prefix_data, char **str, size_t *str_len)
+{
+    int len;
+
+    len = asprintf(str, "%s:%s", lyplg_type_get_prefix(ident->module, format, prefix_data), ident->name);
+    if (len == -1) {
+        return LY_EMEM;
     }
+
+    if (str_len) {
+        *str_len = (size_t)len;
+    }
+    return LY_SUCCESS;
 }
 
-API LY_ERR
-lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
-        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+/**
+ * @brief Convert a string identityref value to matching identity.
+ *
+ * @param[in] value Identityref value.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Context node for resolving the prefixes.
+ * @param[out] ident Found identity.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_str2ident(const char *value, size_t value_len, LY_VALUE_FORMAT format, void *prefix_data,
+        const struct ly_ctx *ctx, const struct lysc_node *ctx_node, struct lysc_ident **ident, struct ly_err_item **err)
 {
-    LY_ERR ret = LY_SUCCESS;
-    struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
-    const char *id_name, *prefix = value, *value_str = value;
-    size_t id_len, prefix_len, str_len;
-    char *str;
-    const struct lys_module *mod = NULL;
+    const char *id_name, *prefix = value;
+    size_t id_len, prefix_len;
+    const struct lys_module *mod;
     LY_ARRAY_COUNT_TYPE u;
-    struct lysc_ident *ident = NULL, *identities, *base;
-
-    *err = NULL;
-
-    /* check hints */
-    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    struct lysc_ident *id, *identities;
 
     /* locate prefix if any */
-    for (prefix_len = 0; (prefix_len < value_len) && (value_str[prefix_len] != ':'); ++prefix_len) {}
+    for (prefix_len = 0; (prefix_len < value_len) && (value[prefix_len] != ':'); ++prefix_len) {}
     if (prefix_len < value_len) {
-        id_name = &value_str[prefix_len + 1];
+        id_name = &value[prefix_len + 1];
         id_len = value_len - (prefix_len + 1);
     } else {
         prefix_len = 0;
-        id_name = value_str;
+        id_name = value;
         id_len = value_len;
     }
 
     if (!id_len) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty identityref value.");
-        goto cleanup;
+        return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty identityref value.");
     }
 
     mod = lyplg_type_identity_module(ctx, ctx_node, prefix, prefix_len, format, prefix_data);
     if (!mod) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
-                "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value_str);
-        goto cleanup;
+        return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+                "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value);
     }
 
+    id = NULL;
     identities = mod->identities;
     LY_ARRAY_FOR(identities, u) {
-        ident = &identities[u]; /* shortcut */
-        if (!ly_strncmp(ident->name, id_name, id_len)) {
+        if (!ly_strncmp(identities[u].name, id_name, id_len)) {
             /* we have match */
+            id = &identities[u];
             break;
         }
     }
-    if (!identities || (u == LY_ARRAY_COUNT(identities))) {
+    if (!id) {
         /* no match */
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+        return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
                 "Invalid identityref \"%.*s\" value - identity not found in module \"%s\".",
-                (int)value_len, value_str, mod->name);
-        goto cleanup;
-    } else if (!mod->implemented) {
-        /* non-implemented module */
-        if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
-            ret = lyplg_type_make_implemented((struct lys_module *)mod, NULL, unres);
-            LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-        } else {
-            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
-                    "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
-                    (int)value_len, value_str, mod->name);
-            goto cleanup;
+                (int)value_len, value, mod->name);
+    }
+
+    *ident = id;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that an identityref is derived from the type base.
+ *
+ * @param[in] ident Identityref.
+ * @param[in] type Identityref type.
+ * @param[in] value String value for logging.
+ * @param[in] value_len Length of @p value.
+ * @param[out] err Error information on error.
+ */
+static LY_ERR
+identityref_check_base(const struct lysc_ident *ident, struct lysc_type_identityref *type, const char *value,
+        size_t value_len, struct ly_err_item **err)
+{
+    LY_ERR ret;
+    size_t str_len;
+    char *str;
+    LY_ARRAY_COUNT_TYPE u;
+    struct lysc_ident *base;
+
+    /* check that the identity matches some of the type's base identities */
+    LY_ARRAY_FOR(type->bases, u) {
+        if (!lyplg_type_identity_isderived(type->bases[u], ident)) {
+            /* we have match */
+            break;
         }
     }
 
-    /* check that the identity matches some of the type's base identities */
-    LY_ARRAY_FOR(type_ident->bases, u) {
-        if (!lyplg_type_identity_isderived(type_ident->bases[u], ident)) {
-            /* we have match */
-            break;
-        }
-    }
-    if (u == LY_ARRAY_COUNT(type_ident->bases)) {
+    /* it does not, generate a nice error */
+    if (u == LY_ARRAY_COUNT(type->bases)) {
         str = NULL;
         str_len = 1;
-        LY_ARRAY_FOR(type_ident->bases, u) {
-            base = type_ident->bases[u];
+        LY_ARRAY_FOR(type->bases, u) {
+            base = type->bases[u];
             str_len += (u ? 2 : 0) + 1 + strlen(base->module->name) + 1 + strlen(base->name) + 1;
             str = ly_realloc(str, str_len);
             sprintf(str + (u ? strlen(str) : 0), "%s\"%s:%s\"", u ? ", " : "", base->module->name, base->name);
@@ -133,29 +171,89 @@
         if (u == 1) {
             ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
                     "Invalid identityref \"%.*s\" value - identity not derived from the base %s.",
-                    (int)value_len, value_str, str);
+                    (int)value_len, value, str);
         } else {
             ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
                     "Invalid identityref \"%.*s\" value - identity not derived from all the bases %s.",
-                    (int)value_len, value_str, str);
+                    (int)value_len, value, str);
         }
         free(str);
-        goto cleanup;
+        return ret;
     }
 
-    storage->ident = ident;
+    return LY_SUCCESS;
+}
 
-    /* get JSON form since there is no canonical */
-    str = identityref_print(storage, LY_VALUE_JSON, NULL);
-    ret = lydict_insert_zc(ctx, str, &storage->_canonical);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+API LY_ERR
+lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
+    char *canon;
+    struct lysc_ident *ident;
+
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* find a matching identity */
+    ret = identityref_str2ident(value, value_len, format, prefix_data, ctx, ctx_node, &ident, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* handle identity in a non-implemented module */
+    if (!ident->module->implemented) {
+        if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+            ret = lyplg_type_make_implemented(ident->module, NULL, unres);
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+                    "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
+                    (int)value_len, (char *)value, ident->module->name);
+            goto cleanup;
+        }
+    }
+
+    /* check that the identity is derived form all the bases */
+    ret = identityref_check_base(ident, type_ident, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ident = ident;
     storage->realtype = type;
 
+    /* store canonical value */
+    if (format == LY_VALUE_CANON) {
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else {
+        /* JSON format with prefix is the canonical one */
+        ret = identityref_ident2str(ident, LY_VALUE_JSON, NULL, &canon, NULL);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
     }
 
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
+    }
     return ret;
 }
 
@@ -163,9 +261,9 @@
 lyplg_type_print_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
-    char *result;
+    char *ret;
 
-    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
+    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
         if (dynamic) {
             *dynamic = 0;
         }
@@ -175,12 +273,12 @@
         return value->_canonical;
     }
 
-    result = identityref_print(value, format, prefix_data);
-    *dynamic = 1;
-    if (value_len) {
-        *value_len = strlen(result);
+    /* print the value in the specific format */
+    if (identityref_ident2str(value->ident, format, prefix_data, &ret, value_len)) {
+        return NULL;
     }
-    return result;
+    *dynamic = 1;
+    return ret;
 }
 
 API LY_ERR
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index ced2bac..c7aa720 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <assert.h>
@@ -31,137 +29,119 @@
 #include "path.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
-API const void *
-lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
-        void *prefix_data, ly_bool *dynamic, size_t *value_len)
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInstanceIdentifier instance-identifier (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier |
+ */
+
+/**
+ * @brief Convert compiled path (instance-identifier) into string.
+ *
+ * @param[in] path Compiled path.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed instance-identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *prefix_data, char **str)
 {
+    LY_ERR ret = LY_SUCCESS;
     LY_ARRAY_COUNT_TYPE u, v;
-    char *result = NULL;
+    char *result = NULL, quot;
+    const struct lys_module *mod = NULL;
+    ly_bool inherit_prefix, d;
+    const char *strval;
 
-    if (!value->target) {
-        /* value was not fully processed */
-        return NULL;
-    }
-
-    if ((format == LY_VALUE_XML) || (format == LY_VALUE_SCHEMA)) {
+    switch (format) {
+    case LY_VALUE_XML:
+    case LY_VALUE_SCHEMA:
+    case LY_VALUE_SCHEMA_RESOLVED:
         /* everything is prefixed */
-        LY_ARRAY_FOR(value->target, u) {
-            ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(value->target[u].node->module, format, prefix_data),
-                    value->target[u].node->name);
-            LY_ARRAY_FOR(value->target[u].predicates, v) {
-                struct ly_path_predicate *pred = &value->target[u].predicates[v];
+        inherit_prefix = 0;
+        break;
+    case LY_VALUE_CANON:
+    case LY_VALUE_JSON:
+    case LY_VALUE_LYB:
+        /* the same prefix is inherited and skipped */
+        inherit_prefix = 1;
+        break;
+    }
 
-                switch (value->target[u].pred_type) {
-                case LY_PATH_PREDTYPE_NONE:
-                    break;
-                case LY_PATH_PREDTYPE_POSITION:
-                    /* position predicate */
-                    ly_strcat(&result, "[%" PRIu64 "]", pred->position);
-                    break;
-                case LY_PATH_PREDTYPE_LIST: {
-                    /* key-predicate */
-                    ly_bool d = 0;
-                    const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
-                    char quot = '\'';
-                    if (strchr(str, quot)) {
-                        quot = '"';
-                    }
-                    ly_strcat(&result, "[%s:%s=%c%s%c]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
-                            pred->key->name, quot, str, quot);
-                    if (d) {
-                        free((char *)str);
-                    }
-                    break;
-                }
-                case LY_PATH_PREDTYPE_LEAFLIST: {
-                    /* leaf-list-predicate */
-                    ly_bool d = 0;
-                    const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
-                    char quot = '\'';
-                    if (strchr(str, quot)) {
-                        quot = '"';
-                    }
-                    ly_strcat(&result, "[.=%c%s%c]", quot, str, quot);
-                    if (d) {
-                        free((char *)str);
-                    }
-                    break;
-                }
-                }
-            }
+    LY_ARRAY_FOR(path, u) {
+        /* new node */
+        if (!inherit_prefix || (mod != path[u].node->module)) {
+            mod = path[u].node->module;
+            ret = ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), path[u].node->name);
+        } else {
+            ret = ly_strcat(&result, "/%s", path[u].node->name);
         }
+        LY_CHECK_GOTO(ret, cleanup);
 
-        *dynamic = 1;
-    } else if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
-        /* generate canonical, only the first node or the node changing module is prefixed */
-        if (!value->_canonical) {
-            struct lys_module *mod = NULL;
-            LY_ARRAY_FOR(value->target, u) {
-                if (mod != value->target[u].node->module) {
-                    mod = value->target[u].node->module;
-                    ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), value->target[u].node->name);
+        /* node predicates */
+        LY_ARRAY_FOR(path[u].predicates, v) {
+            struct ly_path_predicate *pred = &path[u].predicates[v];
+
+            switch (path[u].pred_type) {
+            case LY_PATH_PREDTYPE_NONE:
+                break;
+            case LY_PATH_PREDTYPE_POSITION:
+                /* position predicate */
+                ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
+                break;
+            case LY_PATH_PREDTYPE_LIST:
+                /* key-predicate */
+                strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+                        &d, NULL);
+
+                /* default quote */
+                quot = '\'';
+                if (strchr(strval, quot)) {
+                    quot = '"';
+                }
+                if (inherit_prefix) {
+                    /* always the same prefix as the parent */
+                    ret = ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, strval, quot);
                 } else {
-                    ly_strcat(&result, "/%s", value->target[u].node->name);
+                    ret = ly_strcat(&result, "[%s:%s=%c%s%c]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
+                            pred->key->name, quot, strval, quot);
                 }
-                LY_ARRAY_FOR(value->target[u].predicates, v) {
-                    struct ly_path_predicate *pred = &value->target[u].predicates[v];
+                if (d) {
+                    free((char *)strval);
+                }
+                break;
+            case LY_PATH_PREDTYPE_LEAFLIST:
+                /* leaf-list-predicate */
+                strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+                        &d, NULL);
 
-                    switch (value->target[u].pred_type) {
-                    case LY_PATH_PREDTYPE_NONE:
-                        break;
-                    case LY_PATH_PREDTYPE_POSITION:
-                        /* position predicate */
-                        ly_strcat(&result, "[%" PRIu64 "]", pred->position);
-                        break;
-                    case LY_PATH_PREDTYPE_LIST: {
-                        /* key-predicate */
-                        ly_bool d = 0;
-                        const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
-                        char quot = '\'';
-                        if (strchr(str, quot)) {
-                            quot = '"';
-                        }
-                        ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, str, quot);
-                        if (d) {
-                            free((char *)str);
-                        }
-                        break;
-                    }
-                    case LY_PATH_PREDTYPE_LEAFLIST: {
-                        /* leaf-list-predicate */
-                        ly_bool d = 0;
-                        const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
-                        char quot = '\'';
-                        if (strchr(str, quot)) {
-                            quot = '"';
-                        }
-                        ly_strcat(&result, "[.=%c%s%c]", quot, str, quot);
-                        if (d) {
-                            free((char *)str);
-                        }
-                        break;
-                    }
-                    }
+                /* default quote */
+                quot = '\'';
+                if (strchr(strval, quot)) {
+                    quot = '"';
                 }
+                ret = ly_strcat(&result, "[.=%c%s%c]", quot, strval, quot);
+                if (d) {
+                    free((char *)strval);
+                }
+                break;
             }
 
-            lydict_insert_zc(ctx, result, (const char **)&value->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
         }
+    }
 
-        /* use canonical */
-        result = (char *)value->_canonical;
-        if (dynamic) {
-            *dynamic = 0;
-        }
+cleanup:
+    if (ret) {
+        free(result);
     } else {
-        /* not supported format */
-        return NULL;
+        *str = result;
     }
-
-    if (value_len) {
-        *value_len = strlen(result);
-    }
-    return result;
+    return ret;
 }
 
 API LY_ERR
@@ -171,37 +151,54 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
-    struct ly_path *path = NULL;
+    struct ly_path *path;
+    char *canon;
 
-    /* init */
-    *err = NULL;
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS,  cleanup_value);
+    LY_CHECK_GOTO(ret, cleanup);
 
-    LY_CHECK_GOTO(ret = lyplg_type_lypath_new(ctx, value, value_len, options, format, prefix_data, ctx_node,
-            unres, &path, err), cleanup);
+    /* compile instance-identifier into path */
+    ret = lyplg_type_lypath_new(ctx, value, value_len, options, format, prefix_data, ctx_node,
+            unres, &path, err);
+    LY_CHECK_GOTO(ret, cleanup);
 
-    /* store resolved schema path and init storage */
+    /* init storage */
     storage->_canonical = NULL;
     storage->target = path;
-    path = NULL;
     storage->realtype = type;
 
-    /* generate canonical value */
-    lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+    /* store canonical value */
+    if (format == LY_VALUE_CANON) {
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else {
+        /* JSON format with prefix is the canonical one */
+        ret = instanceid_path2str(path, LY_VALUE_JSON, NULL, &canon);
+        LY_CHECK_GOTO(ret, cleanup);
 
-    /* cleanup */
-cleanup:
-    lyplg_type_lypath_free(ctx, path);
-
-cleanup_value:
-    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
-    if ((ret == LY_SUCCESS) && type_inst->require_instance) {
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_instanceid(ctx, storage);
+    }
+    if (!ret && type_inst->require_instance) {
         /* needs to be resolved */
         return LY_EINCOMPLETE;
     } else {
@@ -225,14 +222,12 @@
     }
 
     /* find the target in data */
-    if (!(ret = ly_path_eval(storage->target, tree, NULL))) {
-        return LY_SUCCESS;
+    if ((ret = ly_path_eval(storage->target, tree, NULL))) {
+        const char *value = lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+        return ly_err_new(err, ret, LYVE_DATA, NULL, NULL, LY_ERRMSG_NOINST, value);
     }
 
-    /* error */
-    const char *value = storage->realtype->plugin->print(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
-
-    return ly_err_new(err, ret, LYVE_DATA, NULL, NULL, LY_ERRMSG_NOINST, value);
+    return LY_SUCCESS;
 }
 
 API LY_ERR
@@ -246,7 +241,7 @@
 
     if (val1 == val2) {
         return LY_SUCCESS;
-    } else if (!val1->target || !val2->target || (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target))) {
+    } else if (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target)) {
         return LY_ENOT;
     }
 
@@ -274,7 +269,8 @@
                     break;
                 case LY_PATH_PREDTYPE_LIST:
                     /* key-predicate */
-                    if ((pred1->key != pred2->key) || ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
+                    if ((pred1->key != pred2->key) ||
+                            ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
                         return LY_ENOT;
                     }
                     break;
@@ -291,20 +287,61 @@
     return LY_SUCCESS;
 }
 
+API const void *
+lyplg_type_print_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+    char *ret;
+
+    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+        if (dynamic) {
+            *dynamic = 0;
+        }
+        if (value_len) {
+            *value_len = strlen(value->_canonical);
+        }
+        return value->_canonical;
+    }
+
+    /* print the value in the specific format */
+    if (instanceid_path2str(value->target, format, prefix_data, &ret)) {
+        return NULL;
+    }
+    *dynamic = 1;
+    if (value_len) {
+        *value_len = strlen(ret);
+    }
+    return ret;
+}
+
 API LY_ERR
 lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
-    LY_CHECK_RET(lydict_insert(ctx, original->_canonical, strlen(original->_canonical), &dup->_canonical));
+    LY_ERR ret;
+
+    memset(dup, 0, sizeof *dup);
+
+    /* canonical value */
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_GOTO(ret, error);
+
+    /* copy path */
+    ret = ly_path_dup(ctx, original->target, &dup->target);
+    LY_CHECK_GOTO(ret, error);
+
     dup->realtype = original->realtype;
-    return ly_path_dup(ctx, original->target, &dup->target);
+    return LY_SUCCESS;
+
+error:
+    lyplg_type_free_instanceid(ctx, dup);
+    return ret;
 }
 
 API void
 lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value)
 {
+    lydict_remove(ctx, value->_canonical);
     ly_path_free(ctx, value->target);
-    value->target = NULL;
-    lyplg_type_free_simple(ctx, value);
 }
 
 /**
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
index c042e49..1cd29b3 100644
--- a/src/plugins_types/integer.c
+++ b/src/plugins_types/integer.c
@@ -12,7 +12,8 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
 
 #include "plugins_types.h"
 
@@ -28,54 +29,94 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @brief LYB value size of each integer type.
+ */
+static size_t integer_lyb_size[] = {
+    [LY_TYPE_INT8] = 1, [LY_TYPE_INT16] = 2, [LY_TYPE_INT32] = 4, [LY_TYPE_INT64] = 8,
+    [LY_TYPE_UINT8] = 1, [LY_TYPE_UINT16] = 2, [LY_TYPE_UINT32] = 4, [LY_TYPE_UINT64] = 8
+};
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInteger (u)int(8/16/32/64) (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 1/2/4/8 | yes | pointer to the specific integer type | integer value |
+ */
+
 API LY_ERR
 lyplg_type_store_int(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
         struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
     int64_t num;
     int base = 1;
-    char *str;
+    char *canon;
     struct lysc_type_num *type_num = (struct lysc_type_num *)type;
 
-    /* check hints */
-    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    switch (type->basetype) {
-    case LY_TYPE_INT8:
-        ret = lyplg_type_parse_int("int8", base, INT64_C(-128), INT64_C(127), value, value_len, &num, err);
-        break;
-    case LY_TYPE_INT16:
-        ret = lyplg_type_parse_int("int16", base, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err);
-        break;
-    case LY_TYPE_INT32:
-        ret = lyplg_type_parse_int("int32", base, INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num, err);
-        break;
-    case LY_TYPE_INT64:
-        ret = lyplg_type_parse_int("int64", base, INT64_C(-9223372036854775807) - INT64_C(1),
-                INT64_C(9223372036854775807), value, value_len, &num, err);
-        break;
-    default:
-        LOGINT(ctx);
-        ret = LY_EINT;
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != integer_lyb_size[type->basetype]) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB signed integer value size %zu (expected %zu).",
+                    value_len, integer_lyb_size[type->basetype]);
+            goto cleanup;
+        }
+
+        /* get the integer */
+        switch (type->basetype) {
+        case LY_TYPE_INT8:
+            num = *(int8_t *)value;
+            break;
+        case LY_TYPE_INT16:
+            num = *(int16_t *)value;
+            break;
+        case LY_TYPE_INT32:
+            num = *(int32_t *)value;
+            break;
+        case LY_TYPE_INT64:
+            num = *(int64_t *)value;
+            break;
+        default:
+            LOGINT(ctx);
+            ret = LY_EINT;
+            goto cleanup;
+        }
+    } else {
+        /* check hints */
+        ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse the integer */
+        switch (type->basetype) {
+        case LY_TYPE_INT8:
+            ret = lyplg_type_parse_int("int8", base, INT64_C(-128), INT64_C(127), value, value_len, &num, err);
+            break;
+        case LY_TYPE_INT16:
+            ret = lyplg_type_parse_int("int16", base, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err);
+            break;
+        case LY_TYPE_INT32:
+            ret = lyplg_type_parse_int("int32", base, INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num, err);
+            break;
+        case LY_TYPE_INT64:
+            ret = lyplg_type_parse_int("int64", base, INT64_C(-9223372036854775807) - INT64_C(1),
+                    INT64_C(9223372036854775807), value, value_len, &num, err);
+            break;
+        default:
+            LOGINT(ctx);
+            ret = LY_EINT;
+        }
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    LY_CHECK_ERR_GOTO(asprintf(&str, "%" PRId64, num) == -1, ret = LY_EMEM, cleanup);
-
-    /* range of the number */
-    if (type_num->range) {
-        ret = lyplg_type_validate_range(type->basetype, type_num->range, num, str, err);
-        LY_CHECK_ERR_GOTO(ret != LY_SUCCESS, free(str), cleanup);
-    }
-
-    /* store everything */
-    ret = lydict_insert_zc(ctx, str, &storage->_canonical);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-
+    /* init storage */
+    storage->_canonical = NULL;
     /* matters for big-endian */
     switch (type->basetype) {
     case LY_TYPE_INT8:
@@ -95,60 +136,113 @@
     }
     storage->realtype = type;
 
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else {
+        /* generate canonical value */
+        LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId64, num) == -1, ret = LY_EMEM, cleanup);
+
+        /* store it */
+        ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* validate range of the number */
+    if (type_num->range) {
+        ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical,
+                strlen(storage->_canonical), err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
     }
     return ret;
 }
 
 API LY_ERR
 lyplg_type_store_uint(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
         struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
     uint64_t num;
     int base = 0;
+    char *canon;
     struct lysc_type_num *type_num = (struct lysc_type_num *)type;
-    char *str;
 
-    /* check hints */
-    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    switch (type->basetype) {
-    case LY_TYPE_UINT8:
-        ret = lyplg_type_parse_uint("uint8", base, UINT64_C(255), value, value_len, &num, err);
-        break;
-    case LY_TYPE_UINT16:
-        ret = lyplg_type_parse_uint("uint16", base, UINT64_C(65535), value, value_len, &num, err);
-        break;
-    case LY_TYPE_UINT32:
-        ret = lyplg_type_parse_uint("uint32", base, UINT64_C(4294967295), value, value_len, &num, err);
-        break;
-    case LY_TYPE_UINT64:
-        ret = lyplg_type_parse_uint("uint64", base, UINT64_C(18446744073709551615), value, value_len, &num, err);
-        break;
-    default:
-        LOGINT(ctx);
-        ret = LY_EINT;
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != integer_lyb_size[type->basetype]) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB unsigned integer value size %zu (expected %zu).",
+                    value_len, integer_lyb_size[type->basetype]);
+            goto cleanup;
+        }
+
+        /* get the integer */
+        switch (type->basetype) {
+        case LY_TYPE_UINT8:
+            num = *(uint8_t *)value;
+            break;
+        case LY_TYPE_UINT16:
+            num = *(uint16_t *)value;
+            break;
+        case LY_TYPE_UINT32:
+            num = *(uint32_t *)value;
+            break;
+        case LY_TYPE_UINT64:
+            num = *(uint64_t *)value;
+            break;
+        default:
+            LOGINT(ctx);
+            ret = LY_EINT;
+            goto cleanup;
+        }
+    } else {
+        /* check hints */
+        ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* parse the integer */
+        switch (type->basetype) {
+        case LY_TYPE_UINT8:
+            ret = lyplg_type_parse_uint("uint8", base, UINT64_C(255), value, value_len, &num, err);
+            break;
+        case LY_TYPE_UINT16:
+            ret = lyplg_type_parse_uint("uint16", base, UINT64_C(65535), value, value_len, &num, err);
+            break;
+        case LY_TYPE_UINT32:
+            ret = lyplg_type_parse_uint("uint32", base, UINT64_C(4294967295), value, value_len, &num, err);
+            break;
+        case LY_TYPE_UINT64:
+            ret = lyplg_type_parse_uint("uint64", base, UINT64_C(18446744073709551615), value, value_len, &num, err);
+            break;
+        default:
+            LOGINT(ctx);
+            ret = LY_EINT;
+        }
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    LY_CHECK_ERR_GOTO(asprintf(&str, "%" PRIu64, num) == -1, ret = LY_EMEM, cleanup);
-
-    /* range of the number */
-    if (type_num->range) {
-        ret = lyplg_type_validate_range(type->basetype, type_num->range, num, str, err);
-        LY_CHECK_ERR_GOTO(ret != LY_SUCCESS, free(str), cleanup);
-    }
-
-    /* store everything */
-    ret = lydict_insert_zc(ctx, str, &storage->_canonical);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-
+    /* init storage */
+    storage->_canonical = NULL;
     /* matters for big-endian */
     switch (type->basetype) {
     case LY_TYPE_UINT8:
@@ -168,9 +262,39 @@
     }
     storage->realtype = type;
 
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else {
+        /* generate canonical value */
+        LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRIu64, num) == -1, ret = LY_EMEM, cleanup);
+
+        /* store it */
+        ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* validate range of the number */
+    if (type_num->range) {
+        ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical,
+                strlen(storage->_canonical), err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
 cleanup:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
     }
     return ret;
 }
diff --git a/src/plugins_types/ip_prefix.c b/src/plugins_types/ip_prefix.c
deleted file mode 100644
index d6f596c..0000000
--- a/src/plugins_types/ip_prefix.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * @file ip_prefix.c
- * @author Michal Vasko <mvasko@cesnet.cz>
- * @brief ietf-inet-types ip-prefix type plugin.
- *
- * Copyright (c) 2019-2021 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
- */
-
-#define _GNU_SOURCE /* asprintf, strdup */
-#include <sys/cdefs.h>
-
-#include "plugins_types.h"
-
-#include <arpa/inet.h>
-#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
-#include <netinet/in.h>
-#include <sys/socket.h>
-#endif
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libyang.h"
-
-#include "common.h"
-#include "compat.h"
-
-/**
- * @brief Canonize an ipv4-prefix value.
- *
- * @param[in] ipv4_prefix Prefix to canonize.
- * @param[out] canonical Canonical format of @p ipv4_prefix.
- * @param[out] err Error structure on error.
- * @return LY_ERR value.
- */
-static LY_ERR
-canonize_ipv4_prefix(const char *ipv4_prefix, char **canonical, struct ly_err_item **err)
-{
-    LY_ERR ret;
-    const char *pref_str;
-    char *ptr, *result = NULL;
-    uint32_t pref, addr_bin, i, mask;
-
-    *canonical = NULL;
-    *err = NULL;
-
-    pref_str = strchr(ipv4_prefix, '/');
-    if (!pref_str) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv4 prefix \"%s\".", ipv4_prefix);
-        goto error;
-    }
-
-    /* learn prefix */
-    pref = strtoul(pref_str + 1, &ptr, 10);
-    if (ptr[0] || (pref > 32)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv4 prefix \"%s\".", ipv4_prefix);
-        goto error;
-    }
-
-    result = malloc(INET_ADDRSTRLEN + 3);
-    if (!result) {
-        ret = LY_EMEM;
-        goto error;
-    }
-
-    /* copy just the network prefix */
-    strncpy(result, ipv4_prefix, pref_str - ipv4_prefix);
-    result[pref_str - ipv4_prefix] = '\0';
-
-    /* convert it to binary form */
-    if (inet_pton(AF_INET, result, (void *)&addr_bin) != 1) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", result);
-        goto error;
-    }
-
-    /* zero host bits */
-    mask = 0;
-    for (i = 0; i < 32; ++i) {
-        mask <<= 1;
-        if (pref > i) {
-            mask |= 1;
-        }
-    }
-    mask = htonl(mask);
-    addr_bin &= mask;
-
-    /* convert back to string */
-    if (!inet_ntop(AF_INET, (void *)&addr_bin, result, INET_ADDRSTRLEN)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address (%s).", strerror(errno));
-        goto error;
-    }
-
-    /* add the prefix */
-    strcat(result, pref_str);
-
-    *canonical = result;
-    return LY_SUCCESS;
-
-error:
-    free(result);
-    return ret;
-}
-
-/**
- * @brief Canonize an ipv6-prefix value.
- *
- * @param[in] ipv6_prefix Prefix to canonize.
- * @param[out] canonical Canonical format of @p ipv6_prefix.
- * @param[out] err Error structure on error.
- * @return LY_ERR value.
- */
-static LY_ERR
-canonize_ipv6_prefix(const char *ipv4_prefix, char **canonical, struct ly_err_item **err)
-{
-    LY_ERR ret;
-    const char *pref_str;
-    char *ptr, *result = NULL;
-    unsigned long int pref, i, j;
-
-    union {
-        struct in6_addr s;
-        uint32_t a[4];
-    } addr_bin;
-    uint32_t mask;
-
-    *canonical = NULL;
-    *err = NULL;
-
-    pref_str = strchr(ipv4_prefix, '/');
-    if (!pref_str) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv6 prefix \"%s\".", ipv4_prefix);
-        goto error;
-    }
-
-    /* learn prefix */
-    pref = strtoul(pref_str + 1, &ptr, 10);
-    if (ptr[0] || (pref > 128)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv6 prefix \"%s\".", ipv4_prefix);
-        goto error;
-    }
-
-    result = malloc(INET6_ADDRSTRLEN + 4);
-    if (!result) {
-        ret = LY_EMEM;
-        goto error;
-    }
-
-    /* copy just the network prefix */
-    strncpy(result, ipv4_prefix, pref_str - ipv4_prefix);
-    result[pref_str - ipv4_prefix] = '\0';
-
-    /* convert it to binary form */
-    if (inet_pton(AF_INET6, result, (void *)&addr_bin.s) != 1) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", result);
-        goto error;
-    }
-
-    /* zero host bits */
-    for (i = 0; i < 4; ++i) {
-        mask = 0;
-        for (j = 0; j < 32; ++j) {
-            mask <<= 1;
-            if (pref > (i * 32) + j) {
-                mask |= 1;
-            }
-        }
-        mask = htonl(mask);
-        addr_bin.a[i] &= mask;
-    }
-
-    /* convert back to string */
-    if (!inet_ntop(AF_INET6, (void *)&addr_bin.s, result, INET6_ADDRSTRLEN)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address (%s).", strerror(errno));
-        goto error;
-    }
-
-    /* add the prefix */
-    strcat(result, pref_str);
-
-    *canonical = result;
-    return LY_SUCCESS;
-
-error:
-    free(result);
-    return ret;
-}
-
-/**
- * @brief Validate, canonize and store value of the ietf-inet-types ipv4-prefix type.
- * Implementation of the ::lyplg_type_store_clb.
- */
-static LY_ERR
-lyplg_type_store_ipv4_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
-        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
-{
-    LY_ERR ret = LY_SUCCESS;
-    char *canonical;
-
-    /* store as a string */
-    ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
-            storage, unres, err);
-    LY_CHECK_RET(ret);
-
-    /* canonize */
-    ret = canonize_ipv4_prefix(storage->_canonical, &canonical, err);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    if (strcmp(canonical, storage->_canonical)) {
-        /* some conversion took place, update the value */
-        lydict_remove(ctx, storage->_canonical);
-        storage->_canonical = NULL;
-        LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, canonical, &storage->_canonical), cleanup);
-    } else {
-        free(canonical);
-    }
-
-cleanup:
-    if (ret) {
-        type->plugin->free(ctx, storage);
-    }
-    return ret;
-}
-
-/**
- * @brief Validate, canonize and store value of the ietf-inet-types ipv6-prefix type.
- * Implementation of the ::lyplg_type_store_clb.
- */
-static LY_ERR
-lyplg_type_store_ipv6_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
-        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
-{
-    LY_ERR ret = LY_SUCCESS;
-    char *canonical;
-
-    /* store as a string */
-    ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
-            storage, unres, err);
-    LY_CHECK_RET(ret);
-
-    /* canonize */
-    ret = canonize_ipv6_prefix(storage->_canonical, &canonical, err);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    if (strcmp(canonical, storage->_canonical)) {
-        /* some conversion took place, update the value */
-        lydict_remove(ctx, storage->_canonical);
-        storage->_canonical = NULL;
-        LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, canonical, &storage->_canonical), cleanup);
-    } else {
-        free(canonical);
-    }
-
-cleanup:
-    if (ret) {
-        type->plugin->free(ctx, storage);
-    }
-    return ret;
-}
-
-/**
- * @brief Plugin information for ip-prefix type implementation.
- *
- * Note that external plugins are supposed to use:
- *
- *   LYPLG_TYPES = {
- */
-const struct lyplg_type_record plugins_ip_prefix[] = {
-    {
-        .module = "ietf-inet-types",
-        .revision = "2013-07-15",
-        .name = "ipv4-prefix",
-
-        .plugin.id = "libyang 2 - ipv4-prefix, version 1",
-        .plugin.store = lyplg_type_store_ipv4_prefix,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
-    },
-    {
-        .module = "ietf-inet-types",
-        .revision = "2013-07-15",
-        .name = "ipv6-prefix",
-
-        .plugin.id = "libyang 2 - ipv6-prefix, version 1",
-        .plugin.store = lyplg_type_store_ipv6_prefix,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_simple,
-        .plugin.print = lyplg_type_print_simple,
-        .plugin.hash = lyplg_type_hash_simple,
-        .plugin.duplicate = lyplg_type_dup_simple,
-        .plugin.free = lyplg_type_free_simple
-    },
-    {0}
-};
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
new file mode 100644
index 0000000..55df218
--- /dev/null
+++ b/src/plugins_types/ipv4_address.c
@@ -0,0 +1,398 @@
+/**
+ * @file ipv4_address.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-address type plugin.
+ *
+ * Copyright (c) 2019-2021 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
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4Address ipv4-address (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 4       | yes       | `struct in_addr *` | IPv4 address in network-byte order |
+ * | string length | no        | `char *` | IPv4 address zone string |
+ */
+
+/**
+ * @brief Stored value structure for ipv4-address
+ */
+struct lyd_value_ipv4_address {
+    struct in_addr addr;
+    const char *zone;
+};
+
+static void lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with optional zone to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in] ctx libyang context with dictionary.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] zone Ipv6 address zone in dictionary.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv4address_str2ip(const char *value, size_t value_len, uint32_t options, const struct ly_ctx *ctx,
+        struct in_addr *addr, const char **zone, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *addr_no_zone;
+    char *zone_ptr = NULL, *addr_dyn = NULL;
+    size_t zone_len;
+
+    /* store zone and get the string IPv4 address without it */
+    if ((zone_ptr = ly_strnchr(value, '%', value_len))) {
+        /* there is a zone index */
+        zone_len = value_len - (zone_ptr - value) - 1;
+        ret = lydict_insert(ctx, zone_ptr + 1, zone_len, zone);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* get the IP without it */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            *zone_ptr = '\0';
+            addr_no_zone = value;
+        } else {
+            addr_dyn = strndup(value, zone_ptr - value);
+            addr_no_zone = addr_dyn;
+        }
+    } else {
+        /* no zone */
+        *zone = NULL;
+
+        /* get the IP terminated with zero */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            /* we can use the value directly */
+            addr_no_zone = value;
+        } else {
+            addr_dyn = strndup(value, value_len);
+            addr_no_zone = addr_dyn;
+        }
+    }
+
+    /* store the IPv4 address in network-byte order */
+    if (!inet_pton(AF_INET, addr_no_zone, addr)) {
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", addr_no_zone);
+        goto cleanup;
+    }
+
+    /* restore the value */
+    if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
+        *zone_ptr = '%';
+    }
+
+cleanup:
+    free(addr_dyn);
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_address(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *value_str = value;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    struct lyd_value_ipv4_address *val;
+    size_t i;
+
+    /* zero storage so we can always free it */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len < 4) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address value size %zu "
+                    "(expected at least 4).", value_len);
+            goto cleanup;
+        }
+        for (i = 4; i < value_len; ++i) {
+            if (!isalnum(value_str[i])) {
+                ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address zone character 0x%x.",
+                        value_str[i]);
+                goto cleanup;
+            }
+        }
+
+        /* allocate the value */
+        val = calloc(1, sizeof *val);
+        LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = val;
+        storage->realtype = type;
+
+        /* store IP address */
+        memcpy(&val->addr, value, sizeof val->addr);
+
+        /* store zone, if any */
+        if (value_len > 4) {
+            ret = lydict_insert(ctx, value + 4, value_len - 4, &val->zone);
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            val->zone = NULL;
+        }
+
+        /* success */
+        goto cleanup;
+    }
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = val;
+    storage->realtype = type;
+
+    /* get the network-byte order address */
+    ret = ipv4address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* store canonical value */
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_ipv4_address(ctx, storage);
+    }
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_address(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_ipv4_address *v1 = val1->ptr, *v2 = val2->ptr;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    /* zones are NULL or in the dictionary */
+    if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr) || (v1->zone != v2->zone)) {
+        return LY_ENOT;
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv4-address ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv4_address(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lyd_value_ipv4_address *val = value->ptr;
+    size_t zone_len;
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        if (!val->zone) {
+            /* address-only, const */
+            *dynamic = 0;
+            if (value_len) {
+                *value_len = sizeof val->addr;
+            }
+            return &val->addr;
+        }
+
+        /* dynamic */
+        zone_len = strlen(val->zone);
+        ret = malloc(sizeof val->addr + zone_len);
+        LY_CHECK_RET(!ret, NULL);
+
+        memcpy(ret, &val->addr, sizeof val->addr);
+        memcpy(ret + sizeof val->addr, val->zone, zone_len);
+
+        *dynamic = 1;
+        if (value_len) {
+            *value_len = sizeof val->addr + zone_len;
+        }
+        return ret;
+    }
+
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* '%' + zone */
+        zone_len = val->zone ? strlen(val->zone) + 1 : 0;
+        ret = malloc(INET_ADDRSTRLEN + zone_len);
+        LY_CHECK_RET(!ret, NULL);
+
+        /* get the address in string */
+        if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+            free(ret);
+            LOGERR(ctx, LY_EVALID, "Failed to get IPv4 address in string (%s).", strerror(errno));
+            return NULL;
+        }
+
+        /* add zone */
+        if (zone_len) {
+            sprintf(ret + strlen(ret), "%%%s", val->zone);
+        }
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv4-address ietf-inet-types type.
+ */
+static const void *
+lyplg_type_hash_ipv4_address(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    /* simply use the (dynamic or const) LYB value */
+    return lyplg_type_print_ipv4_address(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv4_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+    LY_ERR ret;
+    struct lyd_value_ipv4_address *orig_val = original->ptr, *dup_val;
+
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_RET(ret);
+
+    dup_val = calloc(1, sizeof *dup_val);
+    if (!dup_val) {
+        lydict_remove(ctx, dup->_canonical);
+        return LY_EMEM;
+    }
+    memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+    ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
+    if (ret) {
+        lydict_remove(ctx, dup->_canonical);
+        free(dup_val);
+        return ret;
+    }
+
+    dup->ptr = dup_val;
+    dup->realtype = original->realtype;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv4-address ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    struct lyd_value_ipv4_address *val = value->ptr;
+
+    lydict_remove(ctx, value->_canonical);
+    if (val) {
+        lydict_remove(ctx, val->zone);
+        free(val);
+    }
+}
+
+/**
+ * @brief Plugin information for ipv4-address type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ *   LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_address[] = {
+    {
+        .module = "ietf-inet-types",
+        .revision = "2013-07-15",
+        .name = "ipv4-address",
+
+        .plugin.id = "libyang 2 - ipv4-address, version 1",
+        .plugin.store = lyplg_type_store_ipv4_address,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_ipv4_address,
+        .plugin.print = lyplg_type_print_ipv4_address,
+        .plugin.hash = lyplg_type_hash_ipv4_address,
+        .plugin.duplicate = lyplg_type_dup_ipv4_address,
+        .plugin.free = lyplg_type_free_ipv4_address
+    },
+    {0}
+};
diff --git a/src/plugins_types/ipv4_address_no_zone.c b/src/plugins_types/ipv4_address_no_zone.c
new file mode 100644
index 0000000..fe7057a
--- /dev/null
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -0,0 +1,233 @@
+/**
+ * @file ipv4_address_no_zone.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-address-no-zone type plugin.
+ *
+ * Copyright (c) 2019-2021 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
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4AddressNoZone ipv4-address-no-zone (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 4       | yes       | `struct in_addr *` | IPv4 address in network-byte order |
+ */
+
+/**
+ * @brief Stored value structure for ipv4-address
+ */
+struct lyd_value_ipv4_address_no_zone {
+    struct in_addr addr;
+};
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_address_no_zone(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+        size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+
+    /* zero storage so we can always free it */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 4) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address-no-zone value size %zu "
+                    "(expected 4).", value_len);
+            goto cleanup;
+        }
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = NULL;
+        storage->realtype = type;
+
+        /* store IP address as uint32_t value */
+        memcpy(&storage->uint32, value, 4);
+
+        /* success */
+        goto cleanup;
+    }
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = NULL;
+    storage->realtype = type;
+
+    /* we always need a dynamic value */
+    if (!(options & LYPLG_TYPE_STORE_DYNAMIC)) {
+        value = strndup(value, value_len);
+        LY_CHECK_ERR_GOTO(!value, ret = LY_EMEM, cleanup);
+
+        options |= LYPLG_TYPE_STORE_DYNAMIC;
+    }
+
+    /* get the network-byte order address */
+    if (!inet_pton(AF_INET, value, &storage->uint32)) {
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", (char *)value);
+        goto cleanup;
+    }
+
+    /* store canonical value */
+    ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+    options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+    LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
+    }
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    if (val1->uint32 != val2->uint32) {
+        return LY_ENOT;
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv4_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = 4;
+        }
+        return &value->uint32;
+    }
+
+    /* generate canonical value if not already (loaded from LYB) */
+    if (!value->_canonical) {
+        ret = malloc(INET_ADDRSTRLEN);
+        LY_CHECK_RET(!ret, NULL);
+
+        /* get the address in string */
+        if (!inet_ntop(AF_INET, &value->uint32, ret, INET_ADDRSTRLEN)) {
+            free(ret);
+            LOGERR(ctx, LY_EVALID, "Failed to get IPv4 address in string (%s).", strerror(errno));
+            return NULL;
+        }
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_hash_ipv4_address_no_zone(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    /* simply use the LYB value */
+    return lyplg_type_print_ipv4_address_no_zone(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Plugin information for ipv4-address-no-zone type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ *   LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_address_no_zone[] = {
+    {
+        .module = "ietf-inet-types",
+        .revision = "2013-07-15",
+        .name = "ipv4-address-no-zone",
+
+        .plugin.id = "libyang 2 - ipv4-address-no-zone, version 1",
+        .plugin.store = lyplg_type_store_ipv4_address_no_zone,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_ipv4_address_no_zone,
+        .plugin.print = lyplg_type_print_ipv4_address_no_zone,
+        .plugin.hash = lyplg_type_hash_ipv4_address_no_zone,
+        .plugin.duplicate = lyplg_type_dup_simple,
+        .plugin.free = lyplg_type_free_simple
+    },
+    {0}
+};
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
new file mode 100644
index 0000000..4811e7a
--- /dev/null
+++ b/src/plugins_types/ipv4_prefix.c
@@ -0,0 +1,358 @@
+/**
+ * @file ipv4_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-prefix type plugin.
+ *
+ * Copyright (c) 2019-2021 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
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4Prefix ipv4-prefix (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ * | 1 | yes | `uint8_t *` | prefix length up to 32 |
+ */
+
+/**
+ * @brief Stored value structure for ipv4-prefix
+ */
+struct lyd_value_ipv4_prefix {
+    struct in_addr addr;
+    uint8_t prefix;
+};
+
+static void lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with a prefix in string to a binary network-byte order value.
+ *
+ * @param[in] value String to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] addr Allocated address value to fill.
+ * @param[out] prefix Prefix length.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv4prefix_str2ip(const char *value, size_t value_len, struct in_addr *addr, uint8_t *prefix, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *pref_str;
+    char *mask_str = NULL;
+
+    /* it passed the pattern validation */
+    pref_str = ly_strnchr(value, '/', value_len);
+    ly_strntou8(pref_str + 1, value_len - (pref_str + 1 - value), prefix);
+
+    /* get just the network prefix */
+    mask_str = strndup(value, pref_str - value);
+    LY_CHECK_ERR_GOTO(!mask_str, ret = LY_EMEM, cleanup);
+
+    /* convert it to netword-byte order */
+    if (inet_pton(AF_INET, mask_str, addr) != 1) {
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", mask_str);
+        goto cleanup;
+    }
+
+cleanup:
+    free(mask_str);
+    return ret;
+}
+
+/**
+ * @brief Zero host-portion of the IP address.
+ *
+ * @param[in,out] addr IP address.
+ * @param[in] prefix Prefix length.
+ */
+static void
+ipv4prefix_zero_host(struct in_addr *addr, uint8_t prefix)
+{
+    uint32_t i, mask;
+
+    /* zero host bits */
+    mask = 0;
+    for (i = 0; i < 32; ++i) {
+        mask <<= 1;
+        if (prefix > i) {
+            mask |= 1;
+        }
+    }
+    mask = htonl(mask);
+    addr->s_addr &= mask;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-prefix ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    struct lyd_value_ipv4_prefix *val;
+
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 5) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix value size %zu (expected 5).",
+                    value_len);
+            goto cleanup;
+        }
+        if (((uint8_t *)value)[4] > 32) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix prefix length %" PRIu8 ".",
+                    ((uint8_t *)value)[4]);
+            goto cleanup;
+        }
+
+        /* store/allocate value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            val = (void *)value;
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        } else {
+            val = calloc(1, sizeof *val);
+            LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+            memcpy(val, value, value_len);
+        }
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = val;
+        storage->realtype = type;
+
+        /* zero host */
+        ipv4prefix_zero_host(&val->addr, val->prefix);
+
+        /* success */
+        goto cleanup;
+    }
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = val;
+    storage->realtype = type;
+
+    /* get the mask in network-byte order */
+    ret = ipv4prefix_str2ip(value, value_len, &val->addr, &val->prefix, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* zero host */
+    ipv4prefix_zero_host(&val->addr, val->prefix);
+
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    }
+
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_ipv4_prefix(ctx, storage);
+    }
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_prefix(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_ipv4_prefix *v1 = val1->ptr, *v2 = val2->ptr;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    if (memcmp(v1, v2, sizeof *v1)) {
+        return LY_ENOT;
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static const void *
+lyplg_type_print_ipv4_prefix(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lyd_value_ipv4_prefix *val = value->ptr;
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = sizeof *val;
+        }
+        return val;
+    }
+
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* IPv4 mask + '/' + prefix */
+        ret = malloc(INET_ADDRSTRLEN + 3);
+        LY_CHECK_RET(!ret, NULL);
+
+        /* convert back to string */
+        if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+            free(ret);
+            return NULL;
+        }
+
+        /* add the prefix */
+        sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static const void *
+lyplg_type_hash_ipv4_prefix(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    struct lyd_value_ipv4_prefix *val = value->ptr;
+
+    *dynamic = 0;
+    *key_len = sizeof *val;
+    return val;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv4_prefix(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+    LY_ERR ret;
+    struct lyd_value_ipv4_prefix *orig_val = original->ptr, *dup_val;
+
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_RET(ret);
+
+    dup_val = calloc(1, sizeof *dup_val);
+    if (!dup_val) {
+        lydict_remove(ctx, dup->_canonical);
+        return LY_EMEM;
+    }
+    memcpy(dup_val, orig_val, sizeof *orig_val);
+
+    dup->ptr = dup_val;
+    dup->realtype = original->realtype;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static void
+lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    lydict_remove(ctx, value->_canonical);
+    free(value->ptr);
+}
+
+/**
+ * @brief Plugin information for ipv4-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ *   LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_prefix[] = {
+    {
+        .module = "ietf-inet-types",
+        .revision = "2013-07-15",
+        .name = "ipv4-prefix",
+
+        .plugin.id = "libyang 2 - ipv4-prefix, version 1",
+        .plugin.store = lyplg_type_store_ipv4_prefix,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_ipv4_prefix,
+        .plugin.print = lyplg_type_print_ipv4_prefix,
+        .plugin.hash = lyplg_type_hash_ipv4_prefix,
+        .plugin.duplicate = lyplg_type_dup_ipv4_prefix,
+        .plugin.free = lyplg_type_free_ipv4_prefix
+    },
+    {0}
+};
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
index 88e00e5..98660d5 100644
--- a/src/plugins_types/ipv6_address.c
+++ b/src/plugins_types/ipv6_address.c
@@ -36,12 +36,12 @@
 
 /**
  * @page howtoDataLYB LYB Binary Format
- * @subsection howtoDataLYBTypesIPv6Address ietf-inet-types LYB ipv6-address(-no-zone)
+ * @subsection howtoDataLYBTypesIPv6Address ipv6-address (ietf-inet-types)
  *
- * | Size (B) | Mandatory | Meaning |
- * | :------  | :-------: | :-----: |
- * | 16       | yes       | IPv6 address in network-byte order |
- * | variable | no        | IPv6 address zone string |
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 16       | yes       | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | string length | no        | `char *` | IPv6 address zone string |
  */
 
 /**
@@ -52,25 +52,76 @@
     const char *zone;
 };
 
-/**
- * @brief Free an ipv6-address value.
- * Implementation of ::lyplg_type_free_clb.
- */
-static void
-lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value)
-{
-    struct lyd_value_ipv6_address *val = value->ptr;
+static void lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value);
 
-    lydict_remove(ctx, value->_canonical);
-    if (val) {
-        lydict_remove(ctx, val->zone);
-        free(val);
+/**
+ * @brief Convert IP address with optional zone to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in] ctx libyang context with dictionary.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] zone Ipv6 address zone in dictionary.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6address_str2ip(const char *value, size_t value_len, uint32_t options, const struct ly_ctx *ctx,
+        struct in6_addr *addr, const char **zone, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *addr_no_zone;
+    char *zone_ptr = NULL, *addr_dyn = NULL;
+    size_t zone_len;
+
+    /* store zone and get the string IPv6 address without it */
+    if ((zone_ptr = ly_strnchr(value, '%', value_len))) {
+        /* there is a zone index */
+        zone_len = value_len - (zone_ptr - value) - 1;
+        ret = lydict_insert(ctx, zone_ptr + 1, zone_len, zone);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* get the IP without it */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            *zone_ptr = '\0';
+            addr_no_zone = value;
+        } else {
+            addr_dyn = strndup(value, zone_ptr - value);
+            addr_no_zone = addr_dyn;
+        }
+    } else {
+        /* no zone */
+        *zone = NULL;
+
+        /* get the IP terminated with zero */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            /* we can use the value directly */
+            addr_no_zone = value;
+        } else {
+            addr_dyn = strndup(value, value_len);
+            addr_no_zone = addr_dyn;
+        }
     }
+
+    /* store the IPv6 address in network-byte order */
+    if (!inet_pton(AF_INET6, addr_no_zone, addr)) {
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_no_zone);
+        goto cleanup;
+    }
+
+    /* restore the value */
+    if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
+        *zone_ptr = '%';
+    }
+
+cleanup:
+    free(addr_dyn);
+    return ret;
 }
 
 /**
- * @brief Validate and store value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-address ietf-inet-types type.
  */
 static LY_ERR
 lyplg_type_store_ipv6_address(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
@@ -79,37 +130,31 @@
         struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
-    const char *addr_no_zone, *value_str = value;
-    char *zone_ptr = NULL, *addr_dyn = NULL;
+    const char *value_str = value;
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
     struct lyd_value_ipv6_address *val;
-    size_t i, zone_len;
+    size_t i;
+
+    /* zero storage so we can always free it */
+    memset(storage, 0, sizeof *storage);
 
     if (format == LY_VALUE_LYB) {
         /* validation */
-        if (!strcmp(type->plugin->id, "libyang 2 - ipv6-address-no-zone, version 1")) {
-            if (value_len != 16) {
-                ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address-no-zone value size %zu "
-                        "(expected 16).", value_len);
+        if (value_len < 16) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address value size %zu "
+                    "(expected at least 16).", value_len);
+            goto cleanup;
+        }
+        for (i = 16; i < value_len; ++i) {
+            if (!isalnum(value_str[i])) {
+                ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address zone character 0x%x.",
+                        value_str[i]);
                 goto cleanup;
             }
-        } else {
-            if (value_len < 16) {
-                ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address value size %zu "
-                        "(expected at least 16).", value_len);
-                goto cleanup;
-            }
-            for (i = 16; i < value_len; ++i) {
-                if (!isalnum(value_str[i])) {
-                    ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address zone character 0x%x.",
-                            value_str[i]);
-                    goto cleanup;
-                }
-            }
         }
 
         /* allocate the value */
-        val = malloc(sizeof *val);
+        val = calloc(1, sizeof *val);
         LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
 
         /* init storage */
@@ -132,21 +177,14 @@
         goto cleanup;
     }
 
-    /* zero storage so we can always free it */
-    memset(storage, 0, sizeof *storage);
-
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
     LY_CHECK_GOTO(ret, cleanup);
 
     /* length restriction of the string */
     if (type_str->length) {
-        char buf[LY_NUMBER_MAXLEN];
-        size_t char_count = ly_utf8len(value, value_len);
-
         /* value_len is in bytes, but we need number of characters here */
-        snprintf(buf, LY_NUMBER_MAXLEN, "%zu", char_count);
-        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, char_count, buf, err);
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
@@ -163,48 +201,9 @@
     storage->ptr = val;
     storage->realtype = type;
 
-    /* store zone and get the string IPv6 address without it */
-    if ((zone_ptr = ly_strnchr(value_str, '%', value_len))) {
-        /* there is a zone index */
-        zone_len = value_len - (zone_ptr - value_str) - 1;
-        ret = lydict_insert(ctx, zone_ptr + 1, zone_len, &val->zone);
-        LY_CHECK_GOTO(ret, cleanup);
-
-        /* get the IP without it */
-        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-            *zone_ptr = '\0';
-            addr_no_zone = value_str;
-        } else {
-            addr_dyn = strndup(value_str, zone_ptr - value_str);
-            addr_no_zone = addr_dyn;
-        }
-    } else {
-        /* no zone */
-        val->zone = NULL;
-
-        /* get the IP terminated with zero */
-        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-            ((char *)value_str)[value_len] = '\0';
-            addr_no_zone = value_str;
-        } else if (value_str[value_len] != '\0') {
-            addr_dyn = strndup(value_str, value_len);
-            addr_no_zone = addr_dyn;
-        } else {
-            /* we can use the value directly */
-            addr_no_zone = value_str;
-        }
-    }
-
-    /* store the IPv6 address in network-byte order */
-    if (!inet_pton(AF_INET6, addr_no_zone, &val->addr)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_no_zone);
-        goto cleanup;
-    }
-
-    /* restore the value */
-    if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
-        *zone_ptr = '%';
-    }
+    /* get the network-byte order address */
+    ret = ipv6address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
+    LY_CHECK_GOTO(ret, cleanup);
 
     if (format == LY_VALUE_CANON) {
         /* store canonical value */
@@ -219,9 +218,8 @@
     }
 
 cleanup:
-    free(addr_dyn);
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
     }
 
     if (ret) {
@@ -231,8 +229,7 @@
 }
 
 /**
- * @brief Compare 2 values of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv6-address ietf-inet-types type.
  */
 static LY_ERR
 lyplg_type_compare_ipv6_address(const struct lyd_value *val1, const struct lyd_value *val2)
@@ -251,8 +248,7 @@
 }
 
 /**
- * @brief Print a value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv6-address ietf-inet-types type.
  */
 static const void *
 lyplg_type_print_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
@@ -263,15 +259,23 @@
     char *ret;
 
     if (format == LY_VALUE_LYB) {
-        /* binary format is not using cache and will always be dynamic */
-        zone_len = val->zone ? strlen(val->zone) : 0;
+        if (!val->zone) {
+            /* address-only, const */
+            *dynamic = 0;
+            if (value_len) {
+                *value_len = sizeof val->addr;
+            }
+            return &val->addr;
+        }
+
+        /* dynamic */
+        zone_len = strlen(val->zone);
         ret = malloc(sizeof val->addr + zone_len);
         LY_CHECK_RET(!ret, NULL);
 
         memcpy(ret, &val->addr, sizeof val->addr);
-        if (zone_len) {
-            memcpy(ret + sizeof val->addr, val->zone, zone_len);
-        }
+        memcpy(ret + sizeof val->addr, val->zone, zone_len);
+
         *dynamic = 1;
         if (value_len) {
             *value_len = sizeof val->addr + zone_len;
@@ -316,28 +320,17 @@
 }
 
 /**
- * @brief Get hash key of a value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv6-address ietf-inet-types type.
  */
 static const void *
 lyplg_type_hash_ipv6_address(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
 {
-    struct lyd_value_ipv6_address *val = value->ptr;
-
-    if (!val->zone) {
-        /* we can use the IP directly */
-        *dynamic = 0;
-        *key_len = sizeof val->addr;
-        return &val->addr;
-    }
-
-    /* simply use the (dynamic) LYB value */
+    /* simply use the (dynamic or const) LYB value */
     return lyplg_type_print_ipv6_address(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
 }
 
 /**
- * @brief Duplicate a value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv6-address ietf-inet-types type.
  */
 static LY_ERR
 lyplg_type_dup_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
@@ -348,7 +341,7 @@
     ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
     LY_CHECK_RET(ret);
 
-    dup_val = malloc(sizeof *dup_val);
+    dup_val = calloc(1, sizeof *dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
@@ -367,13 +360,28 @@
 }
 
 /**
- * @brief Plugin information for ip-address type implementation.
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv6-address ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    struct lyd_value_ipv6_address *val = value->ptr;
+
+    lydict_remove(ctx, value->_canonical);
+    if (val) {
+        lydict_remove(ctx, val->zone);
+        free(val);
+    }
+}
+
+/**
+ * @brief Plugin information for ipv6-address type implementation.
  *
  * Note that external plugins are supposed to use:
  *
  *   LYPLG_TYPES = {
  */
-const struct lyplg_type_record plugins_ip_address[] = {
+const struct lyplg_type_record plugins_ipv6_address[] = {
     {
         .module = "ietf-inet-types",
         .revision = "2013-07-15",
@@ -388,19 +396,5 @@
         .plugin.duplicate = lyplg_type_dup_ipv6_address,
         .plugin.free = lyplg_type_free_ipv6_address
     },
-    {
-        .module = "ietf-inet-types",
-        .revision = "2013-07-15",
-        .name = "ipv6-address-no-zone",
-
-        .plugin.id = "libyang 2 - ipv6-address-no-zone, version 1",
-        .plugin.store = lyplg_type_store_ipv6_address,
-        .plugin.validate = NULL,
-        .plugin.compare = lyplg_type_compare_ipv6_address,
-        .plugin.print = lyplg_type_print_ipv6_address,
-        .plugin.hash = lyplg_type_hash_ipv6_address,
-        .plugin.duplicate = lyplg_type_dup_ipv6_address,
-        .plugin.free = lyplg_type_free_ipv6_address
-    },
     {0}
 };
diff --git a/src/plugins_types/ipv6_address_no_zone.c b/src/plugins_types/ipv6_address_no_zone.c
new file mode 100644
index 0000000..da526b1
--- /dev/null
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -0,0 +1,325 @@
+/**
+ * @file ipv6_address_no_zone.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-address-no-zone type plugin.
+ *
+ * Copyright (c) 2019-2021 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
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6AddressNoZone ipv6-address-no-zone (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 16       | yes       | `struct in6_addr *` | IPv6 address in network-byte order |
+ */
+
+/**
+ * @brief Stored value structure for ipv6-address-no-zone
+ */
+struct lyd_value_ipv6_address_no_zone {
+    struct in6_addr addr;
+};
+
+static void lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6addressnozone_str2ip(const char *value, size_t value_len, uint32_t options, struct in6_addr *addr, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *addr_str;
+    char *addr_dyn = NULL;
+
+    /* get the IP terminated with zero */
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        /* we can use the value directly */
+        addr_str = value;
+    } else {
+        addr_dyn = strndup(value, value_len);
+        addr_str = addr_dyn;
+    }
+
+    /* store the IPv6 address in network-byte order */
+    if (!inet_pton(AF_INET6, addr_str, addr)) {
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_str);
+        goto cleanup;
+    }
+
+cleanup:
+    free(addr_dyn);
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value,
+        size_t value_len, uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    struct lyd_value_ipv6_address_no_zone *val;
+
+    /* zero storage so we can always free it */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 16) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address-no-zone value size %zu "
+                    "(expected 16).", value_len);
+            goto cleanup;
+        }
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = NULL;
+        storage->realtype = type;
+
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            /* use the value directly */
+            storage->ptr = (void *)value;
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        } else {
+            /* allocate the value */
+            val = calloc(1, sizeof *val);
+            LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+            storage->ptr = val;
+
+            /* store IP address */
+            memcpy(&val->addr, value, sizeof val->addr);
+        }
+
+        /* success */
+        goto cleanup;
+    }
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = val;
+    storage->realtype = type;
+
+    /* get the network-byte order address */
+    ret = ipv6addressnozone_str2ip(value, value_len, options, &val->addr, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    }
+
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_ipv6_address_no_zone(ctx, storage);
+    }
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_ipv6_address_no_zone *v1 = val1->ptr, *v2 = val2->ptr;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
+        return LY_ENOT;
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lyd_value_ipv6_address_no_zone *val = value->ptr;
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = sizeof val->addr;
+        }
+        return &val->addr;
+    }
+
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* '%' + zone */
+        ret = malloc(INET6_ADDRSTRLEN);
+        LY_CHECK_RET(!ret, NULL);
+
+        /* get the address in string */
+        if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+            free(ret);
+            LOGERR(ctx, LY_EVALID, "Failed to get IPv6 address in string (%s).", strerror(errno));
+            return NULL;
+        }
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_hash_ipv6_address_no_zone(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    /* simply use the LYB value */
+    return lyplg_type_print_ipv6_address_no_zone(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+    LY_ERR ret;
+    struct lyd_value_ipv6_address_no_zone *orig_val = original->ptr, *dup_val;
+
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_RET(ret);
+
+    dup_val = calloc(1, sizeof *dup_val);
+    if (!dup_val) {
+        lydict_remove(ctx, dup->_canonical);
+        return LY_EMEM;
+    }
+    memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+
+    dup->ptr = dup_val;
+    dup->realtype = original->realtype;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    struct lyd_value_ipv6_address *val = value->ptr;
+
+    lydict_remove(ctx, value->_canonical);
+    free(val);
+}
+
+/**
+ * @brief Plugin information for ipv6-address-no-zone type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ *   LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_address_no_zone[] = {
+    {
+        .module = "ietf-inet-types",
+        .revision = "2013-07-15",
+        .name = "ipv6-address-no-zone",
+
+        .plugin.id = "libyang 2 - ipv6-address-no-zone, version 1",
+        .plugin.store = lyplg_type_store_ipv6_address_no_zone,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_ipv6_address_no_zone,
+        .plugin.print = lyplg_type_print_ipv6_address_no_zone,
+        .plugin.hash = lyplg_type_hash_ipv6_address_no_zone,
+        .plugin.duplicate = lyplg_type_dup_ipv6_address_no_zone,
+        .plugin.free = lyplg_type_free_ipv6_address_no_zone
+    },
+    {0}
+};
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
new file mode 100644
index 0000000..dddd945
--- /dev/null
+++ b/src/plugins_types/ipv6_prefix.c
@@ -0,0 +1,360 @@
+/**
+ * @file ipv6_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-prefix type plugin.
+ *
+ * Copyright (c) 2019-2021 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
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6Prefix ipv6-prefix (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | 1 | yes | `uint8_t *` | prefix length up to 128 |
+ */
+
+/**
+ * @brief Stored value structure for ipv6-prefix
+ */
+struct lyd_value_ipv6_prefix {
+    struct in6_addr addr;
+    uint8_t prefix;
+};
+
+static void lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with a prefix in string to a binary network-byte order value.
+ *
+ * @param[in] value String to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] addr Allocated address value to fill.
+ * @param[out] prefix Prefix length.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6prefix_str2ip(const char *value, size_t value_len, struct in6_addr *addr, uint8_t *prefix, struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    const char *pref_str;
+    char *mask_str = NULL;
+
+    /* it passed the pattern validation */
+    pref_str = ly_strnchr(value, '/', value_len);
+    ly_strntou8(pref_str + 1, value_len - (pref_str + 1 - value), prefix);
+
+    /* get just the network prefix */
+    mask_str = strndup(value, pref_str - value);
+    LY_CHECK_ERR_GOTO(!mask_str, ret = LY_EMEM, cleanup);
+
+    /* convert it to netword-byte order */
+    if (inet_pton(AF_INET6, mask_str, addr) != 1) {
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", mask_str);
+        goto cleanup;
+    }
+
+cleanup:
+    free(mask_str);
+    return ret;
+}
+
+/**
+ * @brief Zero host-portion of the IP address.
+ *
+ * @param[in,out] addr IP address.
+ * @param[in] prefix Prefix length.
+ */
+static void
+ipv6prefix_zero_host(struct in6_addr *addr, uint8_t prefix)
+{
+    uint32_t i, j, mask;
+
+    /* zero host bits */
+    for (i = 0; i < 4; ++i) {
+        mask = 0;
+        for (j = 0; j < 32; ++j) {
+            mask <<= 1;
+            if (prefix > (i * 32) + j) {
+                mask |= 1;
+            }
+        }
+        mask = htonl(mask);
+        ((uint32_t *)addr->s6_addr)[i] &= mask;
+    }
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-prefix ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_prefix(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+        uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    struct lyd_value_ipv6_prefix *val;
+
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* validation */
+        if (value_len != 17) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix value size %zu (expected 17).",
+                    value_len);
+            goto cleanup;
+        }
+        if (((uint8_t *)value)[16] > 128) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix prefix length %" PRIu8 ".",
+                    ((uint8_t *)value)[16]);
+            goto cleanup;
+        }
+
+        /* store/allocate value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            val = (void *)value;
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        } else {
+            val = calloc(1, sizeof *val);
+            LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+            memcpy(val, value, value_len);
+        }
+
+        /* init storage */
+        storage->_canonical = NULL;
+        storage->ptr = val;
+        storage->realtype = type;
+
+        /* zero host */
+        ipv6prefix_zero_host(&val->addr, val->prefix);
+
+        /* success */
+        goto cleanup;
+    }
+
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* allocate the value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = val;
+    storage->realtype = type;
+
+    /* get the mask in network-byte order */
+    ret = ipv6prefix_str2ip(value, value_len, &val->addr, &val->prefix, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* zero host */
+    ipv6prefix_zero_host(&val->addr, val->prefix);
+
+    if (format == LY_VALUE_CANON) {
+        /* store canonical value */
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    }
+
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_ipv6_prefix(ctx, storage);
+    }
+    return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_prefix(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+    struct lyd_value_ipv6_prefix *v1 = val1->ptr, *v2 = val2->ptr;
+
+    if (val1->realtype != val2->realtype) {
+        return LY_ENOT;
+    }
+
+    if (memcmp(v1, v2, sizeof *v1)) {
+        return LY_ENOT;
+    }
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static const void *
+lyplg_type_print_ipv6_prefix(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+        void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+    struct lyd_value_ipv6_prefix *val = value->ptr;
+    char *ret;
+
+    if (format == LY_VALUE_LYB) {
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = sizeof *val;
+        }
+        return val;
+    }
+
+    /* generate canonical value if not already */
+    if (!value->_canonical) {
+        /* IPv6 mask + '/' + prefix */
+        ret = malloc(INET6_ADDRSTRLEN + 4);
+        LY_CHECK_RET(!ret, NULL);
+
+        /* convert back to string */
+        if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+            free(ret);
+            return NULL;
+        }
+
+        /* add the prefix */
+        sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
+
+        /* store it */
+        if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+            LOGMEM(ctx);
+            return NULL;
+        }
+    }
+
+    /* use the cached canonical value */
+    if (dynamic) {
+        *dynamic = 0;
+    }
+    if (value_len) {
+        *value_len = strlen(value->_canonical);
+    }
+    return value->_canonical;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static const void *
+lyplg_type_hash_ipv6_prefix(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+    struct lyd_value_ipv6_prefix *val = value->ptr;
+
+    *dynamic = 0;
+    *key_len = sizeof *val;
+    return val;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_prefix(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+    LY_ERR ret;
+    struct lyd_value_ipv6_prefix *orig_val = original->ptr, *dup_val;
+
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_RET(ret);
+
+    dup_val = calloc(1, sizeof *dup_val);
+    if (!dup_val) {
+        lydict_remove(ctx, dup->_canonical);
+        return LY_EMEM;
+    }
+    memcpy(dup_val, orig_val, sizeof *orig_val);
+
+    dup->ptr = dup_val;
+    dup->realtype = original->realtype;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static void
+lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+    lydict_remove(ctx, value->_canonical);
+    free(value->ptr);
+}
+
+/**
+ * @brief Plugin information for ipv6-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ *   LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_prefix[] = {
+    {
+        .module = "ietf-inet-types",
+        .revision = "2013-07-15",
+        .name = "ipv6-prefix",
+
+        .plugin.id = "libyang 2 - ipv6-prefix, version 1",
+        .plugin.store = lyplg_type_store_ipv6_prefix,
+        .plugin.validate = NULL,
+        .plugin.compare = lyplg_type_compare_ipv6_prefix,
+        .plugin.print = lyplg_type_print_ipv6_prefix,
+        .plugin.hash = lyplg_type_hash_ipv6_prefix,
+        .plugin.duplicate = lyplg_type_dup_ipv6_prefix,
+        .plugin.free = lyplg_type_free_ipv6_prefix
+    },
+    {0}
+};
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
index 99baf84..10a36ea 100644
--- a/src/plugins_types/leafref.c
+++ b/src/plugins_types/leafref.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <assert.h>
@@ -28,7 +26,14 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
-const struct lyplg_type_record plugins_leafref[];
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesLeafref leafref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------: | :-------: | :--: | :-----: |
+ * | exact same format as the leafref target ||||
+ */
 
 API LY_ERR
 lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
@@ -61,7 +66,7 @@
 lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *type, const struct lyd_node *ctx_node,
         const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
 {
-    LY_ERR ret = LY_SUCCESS;
+    LY_ERR ret;
     struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
     char *errmsg = NULL;
 
@@ -75,12 +80,11 @@
     /* check leafref target existence */
     if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
         ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, errmsg);
-        if (errmsg != NULL) {
-            free(errmsg);
-        }
+        free(errmsg);
+        return ret;
     }
 
-    return ret;
+    return LY_SUCCESS;
 }
 
 API LY_ERR
@@ -111,10 +115,7 @@
 API void
 lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    if (value->realtype->plugin != &plugins_leafref[0].plugin) {
-        /* leafref's realtype is again leafref only in case of incomplete store */
-        value->realtype->plugin->free(ctx, value);
-    }
+    value->realtype->plugin->free(ctx, value);
 }
 
 /**
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
index eb05c7d..94b921e 100644
--- a/src/plugins_types/string.c
+++ b/src/plugins_types/string.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <stdint.h>
@@ -27,6 +25,15 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesString string (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
 API LY_ERR
 lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
@@ -36,41 +43,47 @@
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
 
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
+
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* length restriction of the string */
     if (type_str->length) {
-        char buf[LY_NUMBER_MAXLEN];
-        size_t char_count = ly_utf8len(value, value_len);
-
         /* value_len is in bytes, but we need number of characters here */
-        snprintf(buf, LY_NUMBER_MAXLEN, "%zu", char_count);
-        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, char_count, buf, err);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
     /* pattern restrictions */
     ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
-    LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+    LY_CHECK_GOTO(ret, cleanup);
 
-    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
-        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    } else {
-        ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
-        LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-    }
+    /* init storage */
+    storage->_canonical = NULL;
     storage->ptr = NULL;
     storage->realtype = type;
 
-cleanup:
+    /* store canonical value */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+        options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+        LY_CHECK_GOTO(ret, cleanup);
+    } else {
+        ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
+cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
+    if (ret) {
+        lyplg_type_free_simple(ctx, storage);
+    }
     return ret;
 }
 
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 4223eb6..6f1180e 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <stdint.h>
@@ -28,8 +26,80 @@
 #include "compat.h"
 #include "plugins_internal.h" /* LY_TYPE_*_STR */
 
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesUnion union (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | 4 | yes | `uint32_t *` | index of the resolved type in ::lysc_type_union.types |
+ * | exact same format as the resolved type ||||
+ *
+ * Note that loading union value in this format prevents it from changing its real (resolved) type.
+ */
+
+/**
+ * @brief Store subvalue as a specific type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type Specific union type to use for storing.
+ * @param[in] subvalue Union subvalue structure.
+ * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] ctx_node Context node for prefix resolution.
+ * @param[in] tree Data tree for resolving (validation).
+ * @param[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
 static LY_ERR
-ly_type_union_store_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_subvalue *subvalue,
+union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value_union *subvalue,
+        ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
+        struct ly_err_item **err)
+{
+    LY_ERR ret;
+    void *value;
+    size_t value_len;
+
+    if (subvalue->format == LY_VALUE_LYB) {
+        /* skip the type index */
+        value = ((char *)subvalue->original) + 4;
+        value_len = subvalue->orig_len - 4;
+    } else {
+        value = subvalue->original;
+        value_len = subvalue->orig_len;
+    }
+
+    ret = type->plugin->store(ctx, type, value, value_len, 0, subvalue->format, subvalue->prefix_data, subvalue->hints,
+            subvalue->ctx_node, &subvalue->value, unres, err);
+    LY_CHECK_RET((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), ret);
+
+    if (resolve && (ret == LY_EINCOMPLETE)) {
+        /* we need the value resolved */
+        ret = subvalue->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
+        if (ret) {
+            /* resolve failed, we need to free the stored value */
+            type->plugin->free(ctx, &subvalue->value);
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Find the first valid type for a union value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] types Sized array of union types.
+ * @param[in] subvalue Union subvalue structure.
+ * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] ctx_node Context node for prefix resolution.
+ * @param[in] tree Data tree for resolving (validation).
+ * @param[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
         ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
         struct ly_err_item **err)
 {
@@ -48,31 +118,18 @@
 
     /* use the first usable subtype to store the value */
     for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
-        ret = types[u]->plugin->store(ctx, types[u], subvalue->original, strlen(subvalue->original), 0, subvalue->format,
-                subvalue->prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err);
+        ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err);
         if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
-            if (resolve && (ret == LY_EINCOMPLETE)) {
-                /* we need the value resolved */
-                ret = subvalue->value.realtype->plugin->validate(ctx, types[u], ctx_node, tree, &subvalue->value, err);
-                if (!ret) {
-                    /* store and resolve successful */
-                    break;
-                }
-
-                /* resolve failed, we need to free the stored value */
-                types[u]->plugin->free(ctx, &subvalue->value);
-            } else {
-                /* store successful */
-                break;
-            }
+            break;
         }
+
         ly_err_free(*err);
         *err = NULL;
     }
 
     if (u == LY_ARRAY_COUNT(types)) {
-        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
-                "Invalid union value \"%s\" - no matching subtype found.", subvalue->original);
+        ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid union value \"%.*s\" - no matching subtype found.",
+                (int)subvalue->orig_len, (char *)subvalue->original);
     }
 
     /* restore logging */
@@ -87,56 +144,82 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_union *type_u = (struct lysc_type_union *)type;
-    struct lyd_value_subvalue *subvalue = NULL;
+    struct lyd_value_union *subvalue = NULL;
+    uint32_t type_idx;
 
     *err = NULL;
 
-    /* prepare subvalue storage */
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
+
+    if (format == LY_VALUE_LYB) {
+        /* basic validation */
+        if (value_len < 4) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union value size %zu (expected at least 4).",
+                    value_len);
+            goto cleanup;
+        }
+        type_idx = *(uint32_t *)value;
+        if (type_idx >= LY_ARRAY_COUNT(type_u->types)) {
+            ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union type index %" PRIu32
+                    " (type count " LY_PRI_ARRAY_COUNT_TYPE ").", type_idx, LY_ARRAY_COUNT(type_u->types));
+            goto cleanup;
+        }
+    }
+
+    /* prepare subvalue */
     subvalue = calloc(1, sizeof *subvalue);
     if (!subvalue) {
         ret = ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
-        goto cleanup_value;
+        goto cleanup;
     }
 
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->subvalue = subvalue;
+    storage->realtype = type;
+
     /* remember the original value */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        ret = lydict_insert_zc(ctx, (char *)value, &subvalue->original);
+        subvalue->original = (void *)value;
+        subvalue->orig_len = value_len;
         options &= ~LYPLG_TYPE_STORE_DYNAMIC;
-        LY_CHECK_GOTO(ret, cleanup);
     } else {
-        ret = lydict_insert(ctx, value_len ? value : "", value_len, &subvalue->original);
-        LY_CHECK_GOTO(ret, cleanup);
+        subvalue->original = calloc(1, value_len);
+        LY_CHECK_ERR_GOTO(!subvalue->original, ret = LY_EMEM, cleanup);
+        memcpy(subvalue->original, value, value_len);
+        subvalue->orig_len = value_len;
     }
 
     /* store format-specific data for later prefix resolution */
-    ret = lyplg_type_prefix_data_new(ctx, subvalue->original, value_len, format, prefix_data, &subvalue->format,
+    ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &subvalue->format,
             &subvalue->prefix_data);
     LY_CHECK_GOTO(ret, cleanup);
     subvalue->hints = hints;
     subvalue->ctx_node = ctx_node;
 
-    /* use the first usable subtype to store the value */
-    ret = ly_type_union_store_type(ctx, type_u->types, subvalue, 0, NULL, NULL, unres, err);
-    LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+    if (format == LY_VALUE_LYB) {
+        /* use the specific type to store the value */
+        ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
+        LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+    } else {
+        /* use the first usable subtype to store the value */
+        ret = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, unres, err);
+        LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+    }
+
+    /* store canonical value, if any (use the specific type value) */
+    ret = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical);
+    LY_CHECK_GOTO(ret, cleanup);
 
 cleanup:
-    if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
-        lydict_remove(ctx, subvalue->original);
-        lyplg_type_prefix_data_free(subvalue->format, subvalue->prefix_data);
-        free(subvalue);
-    } else {
-        /* store it as union, the specific type is in the subvalue, but canonical value is the specific type value */
-        ret = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical);
-        LY_CHECK_GOTO(ret, cleanup);
-        storage->subvalue = subvalue;
-        storage->realtype = type;
-    }
-
-cleanup_value:
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-        free((char *)value);
+        free((void *)value);
     }
 
+    if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+        lyplg_type_free_union(ctx, storage);
+    }
     return ret;
 }
 
@@ -146,17 +229,17 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_union *type_u = (struct lysc_type_union *)storage->realtype;
-    struct lyd_value_subvalue *subvalue = storage->subvalue;
+    struct lyd_value_union *val = storage->subvalue;
 
     *err = NULL;
 
-    if (!subvalue->value.realtype->plugin->validate) {
+    if (!val->value.realtype->plugin->validate) {
         /* nothing to resolve */
         return LY_SUCCESS;
     }
 
     /* resolve the stored value */
-    if (!subvalue->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err)) {
+    if (!val->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &val->value, err)) {
         /* resolve successful */
         return LY_SUCCESS;
     }
@@ -168,13 +251,24 @@
     ly_err_free(*err);
     *err = NULL;
 
+    if (val->format == LY_VALUE_LYB) {
+        /* use the specific type to store the value */
+        uint32_t type_idx = *(uint32_t *)val->original;
+        ret = union_store_type(ctx, type_u->types[type_idx], val, 1, ctx_node, tree, NULL, err);
+        LY_CHECK_RET(ret);
+    } else {
+        /* use the first usable subtype to store the value */
+        ret = union_find_type(ctx, type_u->types, val, 1, ctx_node, tree, NULL, err);
+        LY_CHECK_RET(ret);
+    }
+
     /* store and resolve the value */
-    ret = ly_type_union_store_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, err);
+    ret = union_find_type(ctx, type_u->types, val, 1, ctx_node, tree, NULL, err);
     LY_CHECK_RET(ret);
 
-    /* success, update the canonical value */
+    /* success, update the canonical value, if any generated */
     lydict_remove(ctx, storage->_canonical);
-    LY_CHECK_RET(lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical));
+    LY_CHECK_RET(lydict_insert(ctx, val->value._canonical, 0, &storage->_canonical));
     return LY_SUCCESS;
 }
 
@@ -215,34 +309,53 @@
 API LY_ERR
 lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
-    LY_CHECK_RET(lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical));
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_value_union *orig_val = original->subvalue, *dup_val;
 
-    dup->subvalue = calloc(1, sizeof *dup->subvalue);
-    LY_CHECK_ERR_RET(!dup->subvalue, LOGMEM(ctx), LY_EMEM);
-    LY_CHECK_RET(original->subvalue->value.realtype->plugin->duplicate(ctx, &original->subvalue->value, &dup->subvalue->value));
-
-    LY_CHECK_RET(lydict_insert(ctx, original->subvalue->original, strlen(original->subvalue->original),
-            &dup->subvalue->original));
-    dup->subvalue->format = original->subvalue->format;
-    LY_CHECK_RET(lyplg_type_prefix_data_dup(ctx, original->subvalue->format, original->subvalue->prefix_data,
-            &dup->subvalue->prefix_data));
-
+    /* init dup value */
+    memset(dup, 0, sizeof *dup);
     dup->realtype = original->realtype;
-    return LY_SUCCESS;
+
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    dup_val = calloc(1, sizeof *dup_val);
+    LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+    dup->subvalue = dup_val;
+
+    ret = orig_val->value.realtype->plugin->duplicate(ctx, &orig_val->value, &dup_val->value);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    dup_val->original = calloc(1, orig_val->orig_len);
+    LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+    memcpy(dup_val->original, orig_val->original, orig_val->orig_len);
+    dup_val->orig_len = orig_val->orig_len;
+
+    dup_val->format = orig_val->format;
+    ret = lyplg_type_prefix_data_dup(ctx, orig_val->format, orig_val->prefix_data, &dup_val->prefix_data);
+    LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+    if (ret) {
+        lyplg_type_free_union(ctx, dup);
+    }
+    return ret;
 }
 
 API void
 lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value)
 {
+    struct lyd_value_union *val = value->subvalue;
+
     lydict_remove(ctx, value->_canonical);
-    if (value->subvalue) {
-        if (value->subvalue->value.realtype) {
-            value->subvalue->value.realtype->plugin->free(ctx, &value->subvalue->value);
+    if (val) {
+        if (val->value.realtype) {
+            val->value.realtype->plugin->free(ctx, &val->value);
         }
-        lyplg_type_prefix_data_free(value->subvalue->format, value->subvalue->prefix_data);
-        lydict_remove(ctx, value->subvalue->original);
-        free(value->subvalue);
-        value->subvalue = NULL;
+        lyplg_type_prefix_data_free(val->format, val->prefix_data);
+        free(val->original);
+
+        free(val);
     }
 }
 
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index 38e7731..f1a94c3 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -12,8 +12,6 @@
  *     https://opensource.org/licenses/BSD-3-Clause
  */
 
-#define _GNU_SOURCE
-
 #include "plugins_types.h"
 
 #include <assert.h>
@@ -31,6 +29,15 @@
 #include "xpath.h"
 
 /**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesXpath10 xpath1.0 (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------  | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the XPath expression |
+ */
+
+/**
  * @brief Stored value structure for xpath1.0
  */
 struct lyd_value_xpath10 {
@@ -199,44 +206,76 @@
 
 API LY_ERR
 lyplg_type_store_xpath10(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
-        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
-        struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+        uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints,
+        const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+        struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
-    struct lyd_value_xpath10 *xp_val;
-    char *canonical;
+    struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    struct lyd_value_xpath10 *val;
+    char *canon;
 
-    /* store as a string */
-    ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
-            storage, unres, err);
-    LY_CHECK_RET(ret);
+    /* clear storage */
+    memset(storage, 0, sizeof *storage);
 
-    xp_val = calloc(1, sizeof *xp_val);
-    LY_CHECK_ERR_GOTO(!xp_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
-    storage->ptr = xp_val;
+    /* check hints */
+    ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* length restriction of the string */
+    if (type_str->length) {
+        /* value_len is in bytes, but we need number of characters here */
+        ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    /* pattern restrictions */
+    ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* init storage */
+    storage->_canonical = NULL;
+    storage->ptr = NULL;
+    storage->realtype = type;
+
+    /* allocate value */
+    val = calloc(1, sizeof *val);
+    LY_CHECK_ERR_GOTO(!val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+    storage->ptr = val;
 
     /* store format-specific data and context for later prefix resolution */
-    ret = lyplg_type_prefix_data_new(ctx, storage->_canonical, strlen(storage->_canonical), format, prefix_data,
-            &xp_val->format, &xp_val->prefix_data);
+    ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
     LY_CHECK_GOTO(ret, cleanup);
-    xp_val->ctx = ctx;
+    val->ctx = ctx;
 
     /* parse */
-    ret = lyxp_expr_parse(ctx, storage->_canonical, strlen(storage->_canonical), 1, &xp_val->exp);
+    ret = lyxp_expr_parse(ctx, value, value_len, 1, &val->exp);
     LY_CHECK_GOTO(ret, cleanup);
 
-    if (format != LY_VALUE_JSON) {
-        /* generate canonical (JSON) value */
-        ret = xpath10_print_value(xp_val, LY_VALUE_JSON, NULL, &canonical, err);
+    /* store canonical value */
+    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+            ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+            options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+            LY_CHECK_GOTO(ret, cleanup);
+        } else {
+            ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    } else {
+        /* JSON format with prefix is the canonical one */
+        ret = xpath10_print_value(val, LY_VALUE_JSON, NULL, &canon, err);
         LY_CHECK_GOTO(ret, cleanup);
 
-        /* replace the canonical value */
-        lydict_remove(ctx, storage->_canonical);
-        storage->_canonical = NULL;
-        LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, canonical, &storage->_canonical), cleanup);
+        ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+        LY_CHECK_GOTO(ret, cleanup);
     }
 
 cleanup:
+    if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        free((void *)value);
+    }
+
     if (ret) {
         lyplg_type_free_xpath10(ctx, storage);
     }
@@ -244,13 +283,13 @@
 }
 
 API const void *
-lyplg_type_print_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
-    char *str_value;
+    char *ret;
     struct ly_err_item *err = NULL;
 
-    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
+    if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
         /* canonical */
         if (dynamic) {
             *dynamic = 0;
@@ -261,42 +300,45 @@
         return value->_canonical;
     }
 
-    if (xpath10_print_value(value->ptr, format, prefix_data, &str_value, &err)) {
+    /* print in the specific format */
+    if (xpath10_print_value(value->ptr, format, prefix_data, &ret, &err)) {
         if (err) {
-            LOGVAL_ERRITEM(NULL, err);
+            LOGVAL_ERRITEM(ctx, err);
             ly_err_free(err);
         }
-        *dynamic = 0;
         return NULL;
     }
 
     *dynamic = 1;
-    return str_value;
+    if (value_len) {
+        *value_len = strlen(ret);
+    }
+    return ret;
 }
 
 API LY_ERR
 lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     LY_ERR ret = LY_SUCCESS;
-    struct lyd_value_xpath10 *xp_val;
-    const struct lyd_value_xpath10 *xp_val_orig = original->ptr;
+    struct lyd_value_xpath10 *orig_val = original->ptr, *dup_val;
 
+    /* init dup value */
     memset(dup, 0, sizeof *dup);
     dup->realtype = original->realtype;
 
-    ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+    ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
     LY_CHECK_GOTO(ret, cleanup);
 
-    xp_val = calloc(1, sizeof *xp_val);
-    LY_CHECK_ERR_GOTO(!xp_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
-    xp_val->ctx = ctx;
-    dup->ptr = xp_val;
+    dup_val = calloc(1, sizeof *dup_val);
+    LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+    dup_val->ctx = ctx;
+    dup->ptr = dup_val;
 
-    ret = lyxp_expr_dup(ctx, xp_val_orig->exp, &xp_val->exp);
+    ret = lyxp_expr_dup(ctx, orig_val->exp, &dup_val->exp);
     LY_CHECK_GOTO(ret, cleanup);
 
-    xp_val->format = xp_val_orig->format;
-    ret = lyplg_type_prefix_data_dup(ctx, xp_val_orig->format, xp_val_orig->prefix_data, &xp_val->prefix_data);
+    dup_val->format = orig_val->format;
+    ret = lyplg_type_prefix_data_dup(ctx, orig_val->format, orig_val->prefix_data, &dup_val->prefix_data);
     LY_CHECK_GOTO(ret, cleanup);
 
 cleanup:
@@ -309,14 +351,14 @@
 API void
 lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_xpath10 *xp_val = value->ptr;
+    struct lyd_value_xpath10 *val = value->ptr;
 
     lydict_remove(ctx, value->_canonical);
-    if (xp_val) {
-        lyxp_expr_free(ctx, xp_val->exp);
-        lyplg_type_prefix_data_free(xp_val->format, xp_val->prefix_data);
+    if (val) {
+        lyxp_expr_free(ctx, val->exp);
+        lyplg_type_prefix_data_free(val->format, val->prefix_data);
 
-        free(xp_val);
+        free(val);
     }
 }
 
diff --git a/src/tree_data.h b/src/tree_data.h
index 0849197..1fcc38a 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -3,7 +3,7 @@
  * @author Radek Krejci <rkrejci@cesnet.cz>
  * @brief libyang representation of YANG data trees.
  *
- * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 2021 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.
@@ -370,7 +370,7 @@
  * support out-of-the-box (meaning that have a special type plugin). Any derived types inherit the format of its
  * closest type with explicit support (up to a built-in type).
  *
- * @section howtoDataLYBTypes Specific storing of particular data type values
+ * @section howtoDataLYBTypes Format of specific data type values
  */
 
 /**
@@ -535,10 +535,11 @@
         uint32_t uint32;             /**< 32-bit unsigned integer */
         uint64_t uint64;             /**< 64-bit unsigned integer */
         struct lysc_type_bitenum_item *enum_item;  /**< pointer to the definition of the enumeration value */
-        struct lysc_type_bitenum_item **bits_items; /**< list of set pointers to the specification of the set bits ([sized array](@ref sizedarrays)) */
+        struct lyd_value_bits *bits; /**< bits value */
         struct lysc_ident *ident;    /**< pointer to the schema definition of the identityref value */
         struct ly_path *target;      /**< Instance-identifier target path. */
-        struct lyd_value_subvalue *subvalue; /** Union value with some metadata. */
+        struct lyd_value_union *subvalue; /** Union value with some metadata. */
+        struct lyd_value_binary *bin; /** Binary value */
         void *ptr;                   /**< generic data type structure used to store the data */
     };  /**< The union is just a list of shorthands to possible values stored by a type's plugin. libyang itself uses the ::lyd_value.realtype
              plugin's callbacks to work with the data.*/
@@ -555,15 +556,15 @@
 /**
  * @brief Special lyd_value structure for union.
  *
- * Represents data with multiple types (union). Original value is stored in the main lyd_value:canonical_cache while
- * the ::lyd_value_subvalue.value contains representation according to one of the union's types.
- * The ::lyd_value_subvalue.prefix_data provides (possible) mappings from prefixes in the original value to YANG modules.
- * These prefixes are necessary to parse original value to the union's subtypes.
+ * Represents data with multiple types (union). The ::lyd_value_union.value contains representation according to
+ * one of the union's types. The ::lyd_value_union.prefix_data provides (possible) mappings from prefixes in
+ * the original value to YANG modules. These prefixes are necessary to parse original value to the union's subtypes.
  */
-struct lyd_value_subvalue {
+struct lyd_value_union {
     struct lyd_value value;      /**< representation of the value according to the selected union's subtype
-                                      (stored as ::lyd_value.realtype here, in subvalue structure */
-    const char *original;        /**< Original value in the dictionary. */
+                                      (stored as ::lyd_value.realtype here) */
+    void *original;              /**< Original value. */
+    size_t orig_len;             /**< Original value length. */
     LY_VALUE_FORMAT format;      /**< Prefix format of the value. However, this information is also used to decide
                                       whether a value is valid for the specific format or not on later validations
                                       (instance-identifier in XML looks different than in JSON). */
@@ -573,6 +574,30 @@
 };
 
 /**
+ * @brief Special lyd_value structure for bits.
+ *
+ * Note that the allocate memory is rounded to bytes. Meaning that if a type defines a bit with the highest position
+ * 18, for example, only 3 bytes will be allocated and casting to a 4-byte type will not work!
+ */
+struct lyd_value_bits {
+    char *bitmap;                           /**< bitmap of size ::lyplg_type_bits_bitmap_size(), if its value is
+                                                cast to an integer type of the corresponding size, can be used
+                                                directly as a bitmap */
+    struct lysc_type_bitenum_item **items;  /**< list of set pointers to the specification of the set
+                                                bits ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Special lyd_value structure for binary.
+ *
+ * Represents an arbitrary binary value.
+ */
+struct lyd_value_binary {
+    void *data;     /**< binary value itself */
+    size_t size;    /**< size of the @p data value */
+};
+
+/**
  * @brief Metadata structure.
  *
  * The structure provides information about metadata of a data element. Such attributes must map to
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index caa41b3..0b91121 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -17,6 +17,7 @@
 ly_add_utest(NAME int8   SOURCES types/int8.c)
 ly_add_utest(NAME string SOURCES types/string.c)
 ly_add_utest(NAME bits   SOURCES types/bits.c)
+ly_add_utest(NAME binary SOURCES types/binary.c)
 ly_add_utest(NAME inet_types   SOURCES types/inet_types.c)
 ly_add_utest(NAME yang_types   SOURCES types/yang_types.c)
 
diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c
index f0ba388..5b4054d 100644
--- a/tests/utests/data/test_tree_data.c
+++ b/tests/utests/data/test_tree_data.c
@@ -51,6 +51,7 @@
             "   list nexthop {min-elements 1; key \"gateway\";"
             "       leaf gateway {type optional-ip-address;}"
             "   }"
+            "   leaf-list pref {type inet:ipv6-prefix;}"
             "}}";
 
     UTEST_SETUP;
@@ -482,9 +483,11 @@
     assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "cont", 0, &root));
     assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='10.0.0.1']", NULL, LYD_NEW_PATH_UPDATE, NULL));
     assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='2100::1']", NULL, LYD_NEW_PATH_UPDATE, NULL));
+    assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/pref[.='fc00::/64']", NULL, 0, NULL));
 
     assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='10.0.0.1']", 0, NULL));
     assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='2100::1']", 0, NULL));
+    assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/pref[.='fc00::/64']", 0, NULL));
     lyd_free_all(root);
 }
 
diff --git a/tests/utests/data/test_types.c b/tests/utests/data/test_types.c
index c3164da..cddda2a 100644
--- a/tests/utests/data/test_types.c
+++ b/tests/utests/data/test_types.c
@@ -350,6 +350,7 @@
     TEST_TYPE_ERROR("enums", "black", "Invalid enumeration value \"black\".", "1");
 }
 
+#if 0
 static void
 test_binary(void **state)
 {
@@ -403,6 +404,8 @@
     TEST_TYPE_ERROR("binary", "TQ==", "This base64 value must be of length 5.", "1");
 }
 
+#endif
+
 static void
 test_boolean(void **state)
 {
@@ -458,9 +461,9 @@
     lyd_free_all(tree);
 
     /* invalid value */
-    TEST_TYPE_ERROR("empty", "x", "Invalid empty value \"x\".", "1");
+    TEST_TYPE_ERROR("empty", "x", "Invalid empty value length 1.", "1");
 
-    TEST_TYPE_ERROR("empty", " ", "Invalid empty value \" \".", "1");
+    TEST_TYPE_ERROR("empty", " ", "Invalid empty value length 1.", "1");
 }
 
 static void
@@ -1045,7 +1048,7 @@
         /* UTEST(test_string, setup),*/
         /* UTEST(test_bits, setup), */
         UTEST(test_enums, setup),
-        UTEST(test_binary, setup),
+        /* UTEST(test_binary, setup), */
         UTEST(test_boolean, setup),
         UTEST(test_empty, setup),
         UTEST(test_identityref, setup),
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 1b8ed65..90ea8fd 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -318,7 +318,7 @@
     CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/aa:ll");
 
     assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG, NULL));
-    CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value \"x\".).", "Schema location /bb:ll.");
+    CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location /bb:ll.");
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;"
             "leaf-list ll {config false;type string; default one;default two;default one;}}", LYS_IN_YANG, &mod));
@@ -1843,7 +1843,7 @@
     /* invalid */
     assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
             "leaf l {type empty; default x;}}", LYS_IN_YANG, NULL));
-    CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value \"x\".).", "Schema location /aa:l.");
+    CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location /aa:l.");
 
     assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}"
             "leaf l {type mytype;}}", LYS_IN_YANG, NULL));
diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c
new file mode 100644
index 0000000..2fbb6e4
--- /dev/null
+++ b/tests/utests/types/binary.c
@@ -0,0 +1,140 @@
+/**
+ * @file binary.c
+ * @author Michal Vaško <mvasko@cesnet.cz>
+ * @brief test for built-in binary type
+ *
+ * Copyright (c) 2021 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 UTEST HEADER */
+#define  _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+    "module " MOD_NAME " {\n" \
+    "  yang-version 1.1;\n" \
+    "  namespace \"urn:tests:" MOD_NAME "\";\n" \
+    "  prefix pref;\n" \
+    NODES \
+    "}\n"
+
+static void
+test_plugin_store(void **state)
+{
+    const char *val, *dec_val;
+    char bin_val[2];
+    struct ly_err_item *err = NULL;
+    const struct lys_module *mod;
+    struct lyd_value value = {0};
+    struct lyplg_type *type = lyplg_find(LYPLG_TYPE, "", NULL, ly_data_type2str[LY_TYPE_BINARY]);
+    struct lysc_type *lysc_type;
+    LY_ERR ly_ret;
+    const char *schema;
+
+    /* create schema. Prepare common used variables */
+    schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}");
+    UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+    lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+    /* check proper type */
+    assert_string_equal("libyang 2 - binary, version 1", type->id);
+
+    /* check store XML double pad */
+    val = "YWhveQ==";
+    dec_val = "ahoy";
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+            0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    /* single pad */
+    val = "YWhveWo=";
+    dec_val = "ahoyj";
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+            0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    /* no pad */
+    val = "YWhveWoy";
+    dec_val = "ahoyj2";
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+            0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    /* binary data */
+    val = "q80=";
+    bin_val[0] = 0xab;
+    bin_val[1] = 0xcd;
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2);
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, bin_val, 2,
+            0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+    CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2);
+    assert_ptr_equal(value.realtype, lysc_type);
+    type->free(UTEST_LYCTX, &value);
+
+    /*
+     * ERROR TESTS
+     */
+    val = "q80.";
+    err = NULL;
+    ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+    assert_int_equal(LY_EVALID, ly_ret);
+    assert_string_equal(err->msg, "Invalid Base64 character '.'.");
+    ly_err_free(err);
+
+    val = "q80";
+    err = NULL;
+    ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+            0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+    assert_int_equal(LY_EVALID, ly_ret);
+    assert_string_equal(err->msg, "Base64 encoded value length must be divisible by 4.");
+    ly_err_free(err);
+}
+
+int
+main(void)
+{
+    const struct CMUnitTest tests[] = {
+        UTEST(test_plugin_store)
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c
index 437c9ab..7688967 100644
--- a/tests/utests/types/bits.c
+++ b/tests/utests/types/bits.c
@@ -586,13 +586,13 @@
     TEST_SUCCESS_XML("T0", "\n\t", BITS, "");
 
     TEST_ERROR_XML("T0", "twelvea");
-    CHECK_LOG_CTX("Invalid bit value \"twelvea\".",
+    CHECK_LOG_CTX("Invalid bit \"twelvea\".",
             "Schema location /T0:port, line number 1.");
     TEST_ERROR_XML("T0", "twelve t");
-    CHECK_LOG_CTX("Invalid bit value \"t\".",
+    CHECK_LOG_CTX("Invalid bit \"t\".",
             "Schema location /T0:port, line number 1.");
     TEST_ERROR_XML("T0", "ELEVEN");
-    CHECK_LOG_CTX("Invalid bit value \"ELEVEN\".",
+    CHECK_LOG_CTX("Invalid bit \"ELEVEN\".",
             "Schema location /T0:port, line number 1.");
 
     /* empty value  */
diff --git a/tests/utests/types/string.c b/tests/utests/types/string.c
index cbd52d5..749d084 100644
--- a/tests/utests/types/string.c
+++ b/tests/utests/types/string.c
@@ -741,7 +741,7 @@
     TEST_SUCCESS_XML("T1", "abc-de", STRING, "abc-de");
     /* ERROR LENGTH  */
     TEST_ERROR_XML("T1", "p4a&lt;");
-    CHECK_LOG_CTX("Unsatisfied length - string length \"4\" is not allowed.",
+    CHECK_LOG_CTX("Unsatisfied length - string \"p4a<\" length is not allowed.",
             "Schema location /T1:port, line number 1.");
 
     /* TEST DEFAULT VALUE */
@@ -785,7 +785,7 @@
     UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
     TEST_SUCCESS_XML("T_UTF8", "€€€€€",    STRING, "€€€€€");
     TEST_ERROR_XML("T_UTF8", "€€€");
-    CHECK_LOG_CTX("Unsatisfied length - string length \"3\" is not allowed.",
+    CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.",
             "Schema location /T_UTF8:port, line number 1.");
     TEST_ERROR_XML("T_UTF8", "€€€€€€€€");
     CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".",
@@ -869,7 +869,7 @@
     TEST_SUCCESS_JSON("T1", "abc\\n-de", STRING, "abc\n-de");
     /* ERROR LENGTH  */
     TEST_ERROR_JSON("T1", "p4a\u042F");
-    CHECK_LOG_CTX("Unsatisfied length - string length \"4\" is not allowed.",
+    CHECK_LOG_CTX("Unsatisfied length - string \"p4aЯ\" length is not allowed.",
             "Schema location /T1:port, line number 1.");
 
     /* TEST DEFAULT VALUE */
@@ -907,7 +907,7 @@
     UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
     TEST_SUCCESS_JSON("T_UTF8", "€€€€€",    STRING, "€€€€€");
     TEST_ERROR_JSON("T_UTF8", "€€€");
-    CHECK_LOG_CTX("Unsatisfied length - string length \"3\" is not allowed.",
+    CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.",
             "Schema location /T_UTF8:port, line number 1.");
     TEST_ERROR_JSON("T_UTF8", "€€€€€€€€");
     CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".",
@@ -977,7 +977,7 @@
             "yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\" 555 555\">"
             "121</port>";
     CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, model_1);
-    CHECK_LOG_CTX("Unsatisfied length - string length \"3\" is not allowed.",
+    CHECK_LOG_CTX("Unsatisfied length - string \"121\" length is not allowed.",
             "Schema location /T_DIFF:port, line number 1.");
 
     /* diff from default value */
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index 491c22e..905ab16 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -16,7 +16,8 @@
 #define  _UTEST_MAIN_
 #include "../utests.h"
 
-/* LOCAL INCLUDE HEADERS */
+#include <stdlib.h>
+
 #include "libyang.h"
 
 #define MODULE_CREATE_YIN(MOD_NAME, NODES) \
@@ -66,59 +67,41 @@
     /* xml test */
     schema = MODULE_CREATE_YANG("a",
             "leaf l {type yang:date-and-time;}"
-            "leaf l2 {type yang:phys-address;}"
-            "leaf l3 {type yang:mac-address;}"
-            "leaf l4 {type yang:hex-string;}"
-            "leaf l5 {type yang:uuid;}"
-            "leaf l6 {type yang:xpath1.0;}");
+            "leaf l2 {type yang:xpath1.0;}");
     UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
 
     /* date-and-time */
-    TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T23:15:15.88888Z");
-    TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-05-31T23:15:15-08:59");
-    TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-05-31T23:15:15-23:00");
+    TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T21:15:15.88888-02:00");
+    TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T06:14:15-02:00");
+    TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T20:15:15-02:00");
 
     /* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */
-    TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59Z", STRING, "1970-01-01T00:59:59Z");
-    TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59Z", STRING, "1969-12-31T23:59:59Z");
+    TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T00:59:59-02:00");
+    TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1969-12-31T23:59:59-02:00");
+
+    /* canonize */
+    TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-01T23:15:15-02:00");
 
     TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00");
     CHECK_LOG_CTX("Unsatisfied pattern - \"2005-05-31T23:15:15.-08:00\" does not conform to "
             "\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".",
             "Schema location /a:l, line number 1.");
-    TEST_ERROR_XML("a", "l", "2005-02-29T23:15:15-08:00");
-    CHECK_LOG_CTX("Checking date-and-time value \"2005-02-29T23:15:15-08:00\" failed, "
-            "canonical date and time is \"2005-03-01T23:15:15\".",
-            "Schema location /a:l, line number 1.");
-
-    /* phys-address */
-    TEST_SUCCESS_XML("a", "l2", "aa:bb:cc:dd", STRING, "aa:bb:cc:dd");
-    TEST_SUCCESS_XML("a", "l2", "AA:BB:1D:2F:CA:52", STRING, "aa:bb:1d:2f:ca:52");
-
-    /* mac-address */
-    TEST_SUCCESS_XML("a", "l3", "12:34:56:78:9A:BC", STRING, "12:34:56:78:9a:bc");
-
-    /* hex-string */
-    TEST_SUCCESS_XML("a", "l4", "AB:CD:eF:fE:dc:Ba:Ab", STRING, "ab:cd:ef:fe:dc:ba:ab");
-
-    /* uuid */
-    TEST_SUCCESS_XML("a", "l5", "12AbCDef-3456-58cd-9ABC-8796cdACdfEE", STRING, "12abcdef-3456-58cd-9abc-8796cdacdfee");
 
     /* xpath1.0 */
-    TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l6", "/aa:l6[. = '4']", STRING, "/a:l6[. = '4']");
+    TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']", STRING, "/a:l2[. = '4']");
     TEST_SUCCESS_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
-            "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l6",
+            "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l2",
             "/yl:yang-library/yl:datastore/yl:name = 'ds:running'", STRING,
             "/ietf-yang-library:yang-library/ietf-yang-library:datastore/ietf-yang-library:name = 'ietf-datastores:running'");
-    TEST_SUCCESS_XML("a", "l6", "/l6[. = '4']", STRING, "/l6[. = '4']");
+    TEST_SUCCESS_XML("a", "l2", "/l2[. = '4']", STRING, "/l2[. = '4']");
 
-    TEST_ERROR_XML("a", "l6", "/a:l6[. = '4']");
-    CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location /a:l6, line number 1.");
-    TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l6",
+    TEST_ERROR_XML("a", "l2", "/a:l2[. = '4']");
+    CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location /a:l2, line number 1.");
+    TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l2",
             "/yl:yang-library/yl:datastore/yl::name");
-    CHECK_LOG_CTX("Storing value \"/yl:yang-library/yl:datastore/yl::name\" failed.", "Schema location /a:l6, line number 1.",
+    CHECK_LOG_CTX("Storing value \"/yl:yang-library/yl:datastore/yl::name\" failed.", "Schema location /a:l2, line number 1.",
             "Invalid character ':'[34] of expression '/yl:yang-library/yl:datastore/yl::name'.",
-            "Schema location /a:l6, line number 1.");
+            "Schema location /a:l2, line number 1.");
 }
 
 static void
diff --git a/tests/utests/utests.h b/tests/utests/utests.h
index 9be12c3..76f782c 100644
--- a/tests/utests/utests.h
+++ b/tests/utests/utests.h
@@ -21,6 +21,7 @@
 #include <setjmp.h>
 #include <stdarg.h>
 #include <stddef.h>
+#include <stdlib.h>
 
 #include <cmocka.h>
 
@@ -94,6 +95,8 @@
 
     struct ly_in *in;    /**< Input handler */
     struct ly_out *out;  /**< Outpu handler */
+
+    char *orig_tz;       /**< Original "TZ" environment variable value */
 };
 
 /**
@@ -929,9 +932,9 @@
     { \
         const char *arr[] = { __VA_ARGS__ }; \
         LY_ARRAY_COUNT_TYPE arr_size = sizeof(arr) / sizeof(arr[0]); \
-        assert_int_equal(arr_size, LY_ARRAY_COUNT((NODE).bits_items)); \
+        assert_int_equal(arr_size, LY_ARRAY_COUNT(((struct lyd_value_bits *)(NODE).ptr)->items)); \
         for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \
-            assert_string_equal(arr[it], (NODE).bits_items[it]->name); \
+            assert_string_equal(arr[it], ((struct lyd_value_bits *)(NODE).ptr)->items[it]->name); \
         } \
     }
 
@@ -1069,8 +1072,12 @@
  *        Example CHECK_LYD_VALUE(node->value, BINARY, "aGVs\nbG8=");
  * @param[in] NODE           lyd_value variable
  * @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE          expected value data
+ * @param[in] SIZE           expected value data size
 */
-#define CHECK_LYD_VALUE_BINARY(NODE, CANNONICAL_VAL) \
+#define CHECK_LYD_VALUE_BINARY(NODE, CANNONICAL_VAL, VALUE, SIZE) \
+    assert_int_equal((NODE).bin->size, SIZE); \
+    assert_int_equal(0, memcmp((NODE).bin->data, VALUE, SIZE)); \
     assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
     assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
     assert_non_null((NODE).realtype); \
@@ -1262,6 +1269,8 @@
 static int
 utest_setup(void **state)
 {
+    char *cur_tz;
+
     /* setup the logger */
     ly_set_log_clb(_utest_logger, 1);
     ly_log_options(LY_LOLOG | LY_LOSTORE);
@@ -1276,6 +1285,15 @@
     /* clean all errors from the setup - usually warnings regarding the plugins directories */
     UTEST_LOG_CLEAN;
 
+    /* backup timezone, if any */
+    cur_tz = getenv("TZ");
+    if (cur_tz) {
+        current_utest_context->orig_tz = strdup(cur_tz);
+    }
+
+    /* set CET */
+    setenv("TZ", "CET+02:00", 1);
+
     return 0;
 }
 
@@ -1298,10 +1316,16 @@
     /* libyang context */
     ly_ctx_destroy(current_utest_context->ctx);
 
+    if (current_utest_context->orig_tz) {
+        /* restore TZ */
+        setenv("TZ", current_utest_context->orig_tz, 1);
+    }
+
     /* utest context */
     ly_in_free(current_utest_context->in, 0);
     free(current_utest_context->err_msg);
     free(current_utest_context->err_path);
+    free(current_utest_context->orig_tz);
     free(current_utest_context);
     current_utest_context = NULL;