schema compile CHANGE support for decimal64 type
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 0b6713a..683bb6d 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1044,8 +1044,7 @@
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
-    uint64_t fraction_digits:1;      /**< fraction digits specification */
-    uint64_t div:63;                 /**< value for moving decimal point (dividing the stored value to get the real value) */
+    uint8_t fraction_digits;         /**< fraction digits specification */
     struct lysc_range *range;        /**< Optional range limitation */
 };
 
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 84cec3f..998bd59 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -565,9 +565,10 @@
 }
 
 static LY_ERR
-range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, const char *value, size_t *len, char **valcopy)
+range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value, size_t *len, char **valcopy)
 {
-    size_t fraction = 0;
+    size_t fraction = 0, size;
+
     *len = 0;
 
     assert(value);
@@ -585,8 +586,12 @@
     }
 
     if ((basetype != LY_TYPE_DEC64) || (value[*len] != '.') || !isdigit(value[*len + 1])) {
-        *valcopy = strndup(value, *len);
-        return LY_SUCCESS;
+        if (basetype == LY_TYPE_DEC64) {
+            goto decimal;
+        } else {
+            *valcopy = strndup(value, *len);
+            return LY_SUCCESS;
+        }
     }
     fraction = *len;
 
@@ -595,13 +600,32 @@
         ++(*len);
     }
 
-    if (fraction) {
-        *valcopy = malloc(((*len) - 1) * sizeof **valcopy);
+    if (basetype == LY_TYPE_DEC64) {
+decimal:
+        assert(frdigits);
+        if (*len - 1 - fraction > frdigits) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                   "Range boundary \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
+                   *len, value, frdigits);
+            return LY_EINVAL;
+        }
+        if (fraction) {
+            size = (*len) + (frdigits - ((*len) - 1 - fraction));
+        } else {
+            size = (*len) + frdigits + 1;
+        }
+        *valcopy = malloc(size * sizeof **valcopy);
         LY_CHECK_ERR_RET(!(*valcopy), LOGMEM(ctx->ctx), LY_EMEM);
 
-        *valcopy[(*len) - 1] = '\0';
-        memcpy(&(*valcopy)[0], &value[0], fraction);
-        memcpy(&(*valcopy)[fraction], &value[fraction + 1], (*len) - 1 - (fraction + 1));
+        (*valcopy)[size - 1] = '\0';
+        if (fraction) {
+            memcpy(&(*valcopy)[0], &value[0], fraction);
+            memcpy(&(*valcopy)[fraction], &value[fraction + 1], (*len) - 1 - (fraction));
+            memset(&(*valcopy)[(*len) - 1], '0', frdigits - ((*len) - 1 - fraction));
+        } else {
+            memcpy(&(*valcopy)[0], &value[0], *len);
+            memset(&(*valcopy)[*len], '0', frdigits);
+        }
     }
     return LY_SUCCESS;
 }
@@ -623,14 +647,14 @@
 
 static LY_ERR
 range_part_minmax(struct lysc_ctx *ctx, struct lysc_range_part *part, int max, int64_t prev, LY_DATA_TYPE basetype, int first, int length_restr,
-                  const char **value)
+                  uint8_t frdigits, const char **value)
 {
     LY_ERR ret = LY_SUCCESS;
     char *valcopy = NULL;
     size_t len;
 
     if (value) {
-        ret = range_part_check_value_syntax(ctx, basetype, *value, &len, &valcopy);
+        ret = range_part_check_value_syntax(ctx, basetype, frdigits, *value, &len, &valcopy);
         LY_CHECK_GOTO(ret, error);
     }
 
@@ -643,7 +667,7 @@
         } else {
             part->min_u64 = UINT64_C(0);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -656,7 +680,7 @@
         } else {
             part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -668,7 +692,7 @@
         } else {
             part->min_64 = INT64_C(-128);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -680,7 +704,7 @@
         } else {
             part->min_64 = INT64_C(-32768);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -692,7 +716,7 @@
         } else {
             part->min_64 = INT64_C(-2147483648);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -705,7 +729,7 @@
         } else {
             part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -717,7 +741,7 @@
         } else {
             part->min_u64 = UINT64_C(0);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -729,7 +753,7 @@
         } else {
             part->min_u64 = UINT64_C(0);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -741,7 +765,7 @@
         } else {
             part->min_u64 = UINT64_C(0);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -753,7 +777,7 @@
         } else {
             part->min_u64 = UINT64_C(0);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -765,7 +789,7 @@
         } else {
             part->min_u64 = UINT64_C(0);
         }
-        if (!first) {
+        if (!ret && !first) {
             ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
@@ -786,7 +810,8 @@
     } else if (ret == LY_EEXIST) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                        "Invalid %s restriction - values are not in ascending order (%s).",
-                       length_restr ? "length" : "range", valcopy ? valcopy : *value);
+                       length_restr ? "length" : "range",
+                       (valcopy && basetype != LY_TYPE_DEC64) ? valcopy : *value);
     } else if (!ret && value) {
         *value = *value + len;
     }
@@ -795,7 +820,7 @@
 }
 
 static LY_ERR
-lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr,
+lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr, uint8_t frdigits,
                        struct lysc_range *base_range, struct lysc_range **range)
 {
     LY_ERR ret = LY_EVALID;
@@ -836,7 +861,7 @@
             expr += 3;
 
             LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
-            LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, 0, basetype, 1, length_restr, NULL), cleanup);
+            LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, 0, basetype, 1, length_restr, frdigits, NULL), cleanup);
             part->max_64 = part->min_64;
         } else if (*expr == '|') {
             if (!parts || range_expected) {
@@ -863,12 +888,12 @@
             /* number */
             if (range_expected) {
                 part = &parts[LY_ARRAY_SIZE(parts) - 1];
-                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, &expr), cleanup);
+                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, &expr), cleanup);
                 range_expected = 0;
             } else {
                 LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
                 LY_CHECK_GOTO(range_part_minmax(ctx, part, 0, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
-                                                basetype, parts_done ? 0 : 1, length_restr, &expr), cleanup);
+                                                basetype, parts_done ? 0 : 1, length_restr, frdigits, &expr), cleanup);
                 part->max_64 = part->min_64;
             }
 
@@ -885,12 +910,12 @@
             }
             if (range_expected) {
                 part = &parts[LY_ARRAY_SIZE(parts) - 1];
-                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, NULL), cleanup);
+                LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, part->min_64, basetype, 0, length_restr, frdigits, NULL), cleanup);
                 range_expected = 0;
             } else {
                 LY_ARRAY_NEW_GOTO(ctx->ctx, parts, part, ret, cleanup);
                 LY_CHECK_GOTO(range_part_minmax(ctx, part, 1, parts_done ? parts[LY_ARRAY_SIZE(parts) - 2].max_64 : 0,
-                                                basetype, parts_done ? 0 : 1, length_restr, NULL), cleanup);
+                                                basetype, parts_done ? 0 : 1, length_restr, frdigits, NULL), cleanup);
                 part->min_64 = part->max_64;
             }
         } else {
@@ -1432,13 +1457,14 @@
     struct lysc_type_str *str;
     struct lysc_type_bits *bits;
     struct lysc_type_enum *enumeration;
+    struct lysc_type_dec *dec;
 
     switch (basetype) {
     case LY_TYPE_BINARY:
         /* RFC 7950 9.8.1, 9.4.4 - length, number of octets it contains */
         bin = (struct lysc_type_bin*)(*type);
         if (type_p->length) {
-            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1,
+            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
                                          base ? ((struct lysc_type_bin*)base)->length : NULL, &bin->length);
             LY_CHECK_RET(ret);
             if (!tpdfname) {
@@ -1464,16 +1490,15 @@
 
         if (builtin && !type_p->flags) {
             /* type derived from bits built-in type must contain at least one bit */
-            if (!bits->bits) {
-                if (tpdfname) {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing bit substatement for bits type \"%s\".", tpdfname);
-                } else {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing bit substatement for bits type.");
-                    free(*type);
-                    *type = NULL;
-                }
-                return LY_EVALID;
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing bit substatement for bits type \"%s\".",
+                       tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing bit substatement for bits type.");
+                free(*type);
+                *type = NULL;
             }
+            return LY_EVALID;
         }
 
         if (tpdfname) {
@@ -1481,11 +1506,46 @@
             *type = calloc(1, sizeof(struct lysc_type_bits));
         }
         break;
+    case LY_TYPE_DEC64:
+        dec = (struct lysc_type_dec*)(*type);
+
+        /* RFC 7950 9.3.4 - fraction-digits */
+        if (builtin && !type_p->fraction_digits) {
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing fraction-digits substatement for decimal64 type \"%s\".",
+                       tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing fraction-digits substatement for decimal64 type.");
+                free(*type);
+                *type = NULL;
+            }
+            return LY_EVALID;
+        }
+        dec->fraction_digits = type_p->fraction_digits;
+
+        /* RFC 7950 9.2.4 - range */
+        if (type_p->range) {
+            ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, dec->fraction_digits,
+                                         base ? ((struct lysc_type_dec*)base)->range : NULL, &dec->range);
+            LY_CHECK_RET(ret);
+            if (!tpdfname) {
+                COMPILE_ARRAY_GOTO(ctx, type_p->range->exts, dec->range->exts,
+                                   options, u, lys_compile_ext, ret, done);
+            }
+        } else if (base && ((struct lysc_type_dec*)base)->range) {
+            dec->range = lysc_range_dup(ctx->ctx, ((struct lysc_type_dec*)base)->range);
+        }
+
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_dec));
+        }
+        break;
     case LY_TYPE_STRING:
         /* RFC 7950 9.4.4 - length */
         str = (struct lysc_type_str*)(*type);
         if (type_p->length) {
-            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1,
+            ret = lys_compile_type_range(ctx, type_p->length, basetype, 1, 0,
                                          base ? ((struct lysc_type_str*)base)->length : NULL, &str->length);
             LY_CHECK_RET(ret);
             if (!tpdfname) {
@@ -1521,17 +1581,15 @@
 
         if (builtin && !type_p->flags) {
             /* type derived from enumerations built-in type must contain at least one enum */
-            if (!enumeration->enums) {
-                if (tpdfname) {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                           "Missing enum substatement for enumeration type \"%s\".", tpdfname);
-                } else {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing enum substatement for enumeration type.");
-                    free(*type);
-                    *type = NULL;
-                }
-                return LY_EVALID;
+            if (tpdfname) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Missing enum substatement for enumeration type \"%s\".", tpdfname);
+            } else {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "Missing enum substatement for enumeration type.");
+                free(*type);
+                *type = NULL;
             }
+            return LY_EVALID;
         }
 
         if (tpdfname) {
@@ -1550,7 +1608,7 @@
         /* RFC 6020 9.2.4 - range */
         num = (struct lysc_type_num*)(*type);
         if (type_p->range) {
-            ret = lys_compile_type_range(ctx, type_p->range, basetype, 1,
+            ret = lys_compile_type_range(ctx, type_p->range, basetype, 0, 0,
                                          base ? ((struct lysc_type_num*)base)->range : NULL, &num->range);
             LY_CHECK_RET(ret);
             if (!tpdfname) {
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index deb227c..76ef956 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -529,6 +529,9 @@
     case LY_TYPE_BITS:
         FREE_ARRAY(ctx, (struct lysc_type_enum_item*)((struct lysc_type_bits*)type)->bits, lysc_enum_item_free);
         break;
+    case LY_TYPE_DEC64:
+        FREE_MEMBER(ctx, ((struct lysc_type_dec*)type)->range, lysc_range_free);
+        break;
     case LY_TYPE_STRING:
         FREE_MEMBER(ctx, ((struct lysc_type_str*)type)->length, lysc_range_free);
         FREE_ARRAY(ctx, ((struct lysc_type_str*)type)->patterns, lysc_pattern_free);
@@ -626,8 +629,6 @@
 void
 lysc_module_free(struct lysc_module *module, void (*private_destructor)(const struct lysc_node *node, void *priv))
 {
-    (void) private_destructor;
-
     if (module) {
         lysc_module_free_(module);
     }
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index bc4169c..6e7fac9 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -1021,6 +1021,73 @@
     ly_ctx_destroy(ctx, NULL);
 }
 
+static void
+test_type_dec64(void **state)
+{
+    *state = test_type_dec64;
+
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    struct lysc_type *type;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;leaf l {type decimal64 {"
+                                        "fraction-digits 2;range min..max;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+    type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+    assert_non_null(type);
+    assert_int_equal(LY_TYPE_DEC64, type->basetype);
+    assert_int_equal(2, ((struct lysc_type_dec*)type)->fraction_digits);
+    assert_non_null(((struct lysc_type_dec*)type)->range);
+    assert_non_null(((struct lysc_type_dec*)type)->range->parts);
+    assert_int_equal(1, LY_ARRAY_SIZE(((struct lysc_type_dec*)type)->range->parts));
+    assert_int_equal(INT64_C(-9223372036854775807) - INT64_C(1), ((struct lysc_type_dec*)type)->range->parts[0].min_64);
+    assert_int_equal(INT64_C(9223372036854775807), ((struct lysc_type_dec*)type)->range->parts[0].max_64);
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;typedef mytype {type decimal64 {"
+                                        "fraction-digits 2;range '3.14 | 5.1 | 10';}}leaf l {type mytype;}}", LYS_IN_YANG));
+    assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+    type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+    assert_non_null(type);
+    assert_int_equal(LY_TYPE_DEC64, type->basetype);
+    assert_int_equal(2, ((struct lysc_type_dec*)type)->fraction_digits);
+    assert_non_null(((struct lysc_type_dec*)type)->range);
+    assert_non_null(((struct lysc_type_dec*)type)->range->parts);
+    assert_int_equal(3, LY_ARRAY_SIZE(((struct lysc_type_dec*)type)->range->parts));
+    assert_int_equal(314, ((struct lysc_type_dec*)type)->range->parts[0].min_64);
+    assert_int_equal(314, ((struct lysc_type_dec*)type)->range->parts[0].max_64);
+    assert_int_equal(510, ((struct lysc_type_dec*)type)->range->parts[1].min_64);
+    assert_int_equal(510, ((struct lysc_type_dec*)type)->range->parts[1].max_64);
+    assert_int_equal(1000, ((struct lysc_type_dec*)type)->range->parts[2].min_64);
+    assert_int_equal(1000, ((struct lysc_type_dec*)type)->range->parts[2].max_64);
+
+    /* invalid cases */
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 0;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid value \"0\" of \"fraction-digits\". Line number 1.");
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits -1;}}}", LYS_IN_YANG));
+    logbuf_assert("Invalid value \"-1\" of \"fraction-digits\". Line number 1.");
+    assert_null(lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64 {fraction-digits 19;}}}", LYS_IN_YANG));
+    logbuf_assert("Value \"19\" is out of \"fraction-digits\" bounds. Line number 1.");
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa; leaf l {type decimal64;}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Missing fraction-digits substatement for decimal64 type.");
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb; leaf l {type decimal64 {fraction-digits 2;"
+                                        "range '3.142';}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Range boundary \"3.142\" of decimal64 type exceeds defined number (2) of fraction digits.");
+
+    assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc; leaf l {type decimal64 {fraction-digits 2;"
+                                        "range '4 | 3.14';}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid range restriction - values are not in ascending order (3.14).");
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
@@ -1032,6 +1099,7 @@
         cmocka_unit_test_setup_teardown(test_type_pattern, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_type_enum, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_type_bits, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_type_dec64, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_node_container, logger_setup, logger_teardown),
     };