plugins types FEATURE decimal64 LYB value support
diff --git a/src/plugins_types.h b/src/plugins_types.h
index a2005e2..78da33d 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -685,8 +685,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,
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
index 0e36811..bd11313 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 = malloc(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, strlen(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;
 }