plugins types FEATURE bits LYB value support
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 56e6ebc..28ec7af 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.
@@ -603,22 +625,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);
 
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
index a74ada4..bac38e2 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 = malloc(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/tree_data.h b/src/tree_data.h
index f6ecbf1..3d5ddc7 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -535,7 +535,7 @@
         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. */
@@ -573,6 +573,20 @@
 };
 
 /**
+ * @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.
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/utests.h b/tests/utests/utests.h
index 9b15c05..e71a8d7 100644
--- a/tests/utests/utests.h
+++ b/tests/utests/utests.h
@@ -929,9 +929,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); \
         } \
     }