types FEATURE support for integer built-in types in data
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 389a8db..21aaf3e 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -236,7 +236,7 @@
                 value_len = 0;
             }
             ret = lyd_value_validate((struct lyd_node_term*)cur, value, value_len,
-                                      LY_TYPE_VALIDATE_CANONIZE | (dynamic ? LY_TYPE_VALIDATE_DYNAMIC : 0));
+                                      LY_TYPE_OPTS_VALIDATE | LY_TYPE_OPTS_CANONIZE | (dynamic ? LY_TYPE_OPTS_DYNAMIC : 0) | LY_TYPE_OPTS_STORE);
             LY_CHECK_GOTO(ret, cleanup);
         } else if (snode->nodetype & LYD_NODE_INNER) {
             int dynamic = 0;
diff --git a/src/plugins_types.c b/src/plugins_types.c
index c8ea09f..e81f879 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -14,6 +14,7 @@
 #include "common.h"
 
 #include <ctype.h>
+#include <errno.h>
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -24,6 +25,90 @@
 #include "tree_schema.h"
 
 API LY_ERR
+parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err)
+{
+    char *errmsg = NULL;
+    char *strptr = NULL;
+    int64_t i;
+
+    LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+    if (!value || !value[0] || !value_len) {
+        asprintf(&errmsg, "Invalid empty %s value.", datatype);
+        goto error;
+    }
+
+    /* convert to 64-bit integer, all the redundant characters are handled */
+    errno = 0;
+
+    /* parse the value */
+    i = strtoll(value, &strptr, base);
+    if (errno || (i < min) || (i > max)) {
+        goto error;
+    } else if (strptr && *strptr && strptr < value + value_len) {
+        while (isspace(*strptr)) {
+            ++strptr;
+        }
+        if (*strptr && strptr < value + value_len) {
+            goto error;
+        }
+    }
+
+    if (ret) {
+        *ret = i;
+    }
+    return LY_SUCCESS;
+
+error:
+    if (!errmsg) {
+        asprintf(&errmsg, "Invalid %s value %.*s.", datatype, (int)value_len, value);
+    }
+    *err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    return LY_EVALID;;
+}
+
+API LY_ERR
+parse_uint(const char *datatype, int base, uint64_t min, uint64_t max, const char *value, size_t value_len, uint64_t *ret, struct ly_err_item **err)
+{
+    char *errmsg = NULL;
+    char *strptr = NULL;
+    uint64_t u;
+
+    LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+    if (!value || !value[0] || !value_len) {
+        asprintf(&errmsg, "Invalid empty %s value.", datatype);
+        goto error;
+    }
+
+    *err = NULL;
+    errno = 0;
+    u = strtoull(value, &strptr, base);
+    if (errno || (u < min) || (u > max)) {
+        goto error;
+    } else if (strptr && *strptr && strptr < value + value_len) {
+        while (isspace(*strptr)) {
+            ++strptr;
+        }
+        if (*strptr && strptr < value + value_len) {
+            goto error;
+        }
+    } else if (u != 0 && value[0] == '-') {
+        goto error;
+    }
+
+    if (ret) {
+        *ret = u;
+    }
+    return LY_SUCCESS;
+
+error:
+    if (!errmsg) {
+        asprintf(&errmsg, "Invalid %s value %.*s.", datatype, (int)value_len, value);
+    }
+    *err = ly_err_new(LY_LLERR, LY_EINVAL, LYVE_RESTRICTION, errmsg, NULL, NULL);
+    return LY_EVALID;;
+}
+
+API LY_ERR
 ly_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, struct ly_err_item **err)
 {
     unsigned int u;
@@ -37,8 +122,8 @@
                     errmsg = strdup(range->emsg);
                 } else {
                     asprintf(&errmsg, "%s \"%"PRIu64"\" does not satisfy the %s constraint.",
-                           (basetype & (LY_TYPE_BINARY | LY_TYPE_STRING)) ? "Length" : "Value", (uint64_t)value,
-                           (basetype & (LY_TYPE_BINARY | LY_TYPE_STRING)) ? "length" : "range");
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "Length" : "Value", (uint64_t)value,
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "length" : "range");
                 }
                 goto error;
             } else if ((uint64_t)value <= range->parts[u].max_u64) {
@@ -50,8 +135,8 @@
                     errmsg = strdup(range->emsg);
                 } else {
                     asprintf(&errmsg, "%s \"%"PRIu64"\" does not satisfy the %s constraint.",
-                           (basetype & (LY_TYPE_BINARY | LY_TYPE_STRING)) ? "Length" : "Value", (uint64_t)value,
-                           (basetype & (LY_TYPE_BINARY | LY_TYPE_STRING)) ? "length" : "range");
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "Length" : "Value", (uint64_t)value,
+                           (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? "length" : "range");
                 }
                 goto error;
             }
@@ -86,6 +171,164 @@
     return LY_EVALID;
 }
 
+static LY_ERR
+ly_type_parse_int_builtin(LY_DATA_TYPE basetype, const char *value, size_t value_len, int options, int64_t *val, struct ly_err_item **err)
+{
+    switch (basetype) {
+    case LY_TYPE_INT8:
+        return parse_int("int16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, __INT64_C(-128), __INT64_C(127), value, value_len, val, err);
+    case LY_TYPE_INT16:
+        return parse_int("int16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, __INT64_C(-32768), __INT64_C(32767), value, value_len, val, err);
+    case LY_TYPE_INT32:
+        return parse_int("int32", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10,
+                         __INT64_C(-2147483648), __INT64_C(2147483647), value, value_len, val, err);
+    case LY_TYPE_INT64:
+        return parse_int("int64", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10,
+                         __INT64_C(-9223372036854775807) - __INT64_C(1), __INT64_C(9223372036854775807), value, value_len, val, err);
+    default:
+        LOGINT(NULL);
+        return LY_EINVAL;
+    }
+}
+/**
+ * @brief Validate and canonize value of the YANG built-in signed integer types.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_int(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                     const char **canonized, struct ly_err_item **err, void **priv)
+{
+    int64_t i;
+    struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+
+    LY_CHECK_RET(ly_type_parse_int_builtin(type->basetype, value, value_len, options, &i, err));
+
+    /* range of the number */
+    if (type_num->range) {
+        LY_CHECK_RET(ly_type_validate_range(type->basetype, type_num->range, i, err));
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        char *str;
+        asprintf(&str, "%"PRId64, i);
+        *canonized = lydict_insert_zc(ctx, str);
+    }
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* save for the store callback */
+        *priv = malloc(sizeof i);
+        *(int64_t*)(*priv) = i;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Store value of the YANG built-in signed integer types.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_int(struct ly_ctx *UNUSED(ctx), struct lysc_type *type, int options,
+                  struct lyd_value *value, struct ly_err_item **err, void **priv)
+{
+    int64_t i;
+
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_int() */
+        i = *(int64_t*)(*priv);
+        free(*priv);
+    } else {
+        LY_CHECK_RET(ly_type_parse_int_builtin(type->basetype, value->canonized, strlen(value->canonized), options, &i, err));
+    }
+
+    /* store the result */
+    value->int64 = i;
+
+    return LY_SUCCESS;
+}
+
+static LY_ERR
+ly_type_parse_uint_builtin(LY_DATA_TYPE basetype, const char *value, size_t value_len, int options, uint64_t *val, struct ly_err_item **err)
+{
+    switch (basetype) {
+    case LY_TYPE_UINT8:
+        return parse_uint("uint16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, __UINT64_C(255), value, value_len, val, err);
+    case LY_TYPE_UINT16:
+        return parse_uint("uint16", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, __UINT64_C(65535), value, value_len, val, err);
+    case LY_TYPE_UINT32:
+        return parse_uint("uint32", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, __UINT64_C(4294967295), value, value_len, val, err);
+    case LY_TYPE_UINT64:
+        return parse_uint("uint64", (options & LY_TYPE_OPTS_SCHEMA) ? 0 : 10, 0, __UINT64_C(18446744073709551615), value, value_len, val, err);
+    default:
+        LOGINT(NULL);
+        return LY_EINVAL;
+    }
+}
+/**
+ * @brief Validate and canonize value of the YANG built-in unsigned integer types.
+ *
+ * Implementation of the ly_type_validate_clb.
+ */
+static LY_ERR
+ly_type_validate_uint(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
+                      const char **canonized, struct ly_err_item **err, void **priv)
+{
+    uint64_t u;
+    struct lysc_type_num* type_num = (struct lysc_type_num*)type;
+
+    LY_CHECK_RET(ly_type_parse_uint_builtin(type->basetype, value, value_len, options, &u, err));
+
+    /* range of the number */
+    if (type_num->range) {
+        LY_CHECK_RET(ly_type_validate_range(type->basetype, type_num->range, u, err));
+    }
+
+    if (options & LY_TYPE_OPTS_CANONIZE) {
+        char *str;
+        asprintf(&str, "%"PRIu64, u);
+        *canonized = lydict_insert_zc(ctx, str);
+    }
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
+        free((char*)value);
+    }
+    if (options & LY_TYPE_OPTS_STORE) {
+        /* save for the store callback */
+        *priv = malloc(sizeof u);
+        *(uint64_t*)(*priv) = u;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Store value of the YANG built-in unsigned integer types.
+ *
+ * Implementation of the ly_type_store_clb.
+ */
+static LY_ERR
+ly_type_store_uint(struct ly_ctx *UNUSED(ctx), struct lysc_type *type, int options,
+                   struct lyd_value *value, struct ly_err_item **err, void **priv)
+{
+    uint64_t u;
+
+    if (options & LY_TYPE_OPTS_VALIDATE) {
+        /* the value was prepared by ly_type_validate_uint() */
+        u = *(uint64_t*)(*priv);
+        free(*priv);
+    } else {
+        LY_CHECK_RET(ly_type_parse_uint_builtin(type->basetype, value->canonized, strlen(value->canonized), options, &u, err));
+    }
+
+    /* store the result */
+    value->uint64 = u;
+
+    return LY_SUCCESS;
+}
+
 /**
  * @brief Validate and canonize value of the YANG built-in binary type.
  *
@@ -93,7 +336,7 @@
  */
 static LY_ERR
 ly_type_validate_binary(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                        const char **canonized, struct ly_err_item **err)
+                        const char **canonized, struct ly_err_item **err, void **UNUSED(priv))
 {
     size_t start = 0, stop = 0, count = 0, u, termination = 0;
     struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
@@ -155,17 +398,17 @@
         LY_CHECK_RET(ly_type_validate_range(LY_TYPE_BINARY, type_bin->length, len, err));
     }
 
-    if (options & LY_TYPE_VALIDATE_CANONIZE) {
+    if (options & LY_TYPE_OPTS_CANONIZE) {
         if (start != 0 || stop != value_len) {
             *canonized = lydict_insert_zc(ctx, strndup(&value[start], stop + 1 - start));
-        } else if (options & LY_TYPE_VALIDATE_DYNAMIC) {
+        } else if (options & LY_TYPE_OPTS_DYNAMIC) {
             *canonized = lydict_insert_zc(ctx, (char*)value);
             value = NULL;
         } else {
             *canonized = lydict_insert(ctx, value_len ? value : "", value_len);
         }
     }
-    if (options & LY_TYPE_VALIDATE_DYNAMIC) {
+    if (options & LY_TYPE_OPTS_DYNAMIC) {
         free((char*)value);
     }
 
@@ -182,10 +425,10 @@
 struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT] = {
     {0}, /* LY_TYPE_UNKNOWN */
     {.type = LY_TYPE_BINARY, .validate = ly_type_validate_binary, .store = NULL},
-    {0}, /* TODO LY_TYPE_UINT8 */
-    {0}, /* TODO LY_TYPE_UINT16 */
-    {0}, /* TODO LY_TYPE_UINT32 */
-    {0}, /* TODO LY_TYPE_UINT64 */
+    {.type = LY_TYPE_UINT8, .validate = ly_type_validate_uint, .store = ly_type_store_uint},
+    {.type = LY_TYPE_UINT16, .validate = ly_type_validate_uint, .store = ly_type_store_uint},
+    {.type = LY_TYPE_UINT32, .validate = ly_type_validate_uint, .store = ly_type_store_uint},
+    {.type = LY_TYPE_UINT64, .validate = ly_type_validate_uint, .store = ly_type_store_uint},
     {0}, /* TODO LY_TYPE_STRING */
     {0}, /* TODO LY_TYPE_BITS */
     {0}, /* TODO LY_TYPE_BOOL */
@@ -196,9 +439,9 @@
     {0}, /* TODO LY_TYPE_INST */
     {0}, /* TODO LY_TYPE_LEAFREF */
     {0}, /* TODO LY_TYPE_UNION */
-    {0}, /* TODO LY_TYPE_INT8 */
-    {0}, /* TODO LY_TYPE_INT16 */
-    {0}, /* TODO LY_TYPE_INT32 */
-    {0}  /* TODO LY_TYPE_INT64 */
+    {.type = LY_TYPE_INT8, .validate = ly_type_validate_int, .store = ly_type_store_int},
+    {.type = LY_TYPE_INT16, .validate = ly_type_validate_int, .store = ly_type_store_int},
+    {.type = LY_TYPE_INT32, .validate = ly_type_validate_int, .store = ly_type_store_int},
+    {.type = LY_TYPE_INT64, .validate = ly_type_validate_int, .store = ly_type_store_int},
 };
 
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 0a84f3c..c106b64 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -47,16 +47,20 @@
 void ly_err_free(void *ptr);
 
 /**
- * @defgroup plugintypevalidateopts Options for type plugin validation callback.
- * Options applicable to ly_type_validate_clb().
+ * @defgroup plugintypeopts Options for type plugin callbacks. The same set of the options is passed to all the type's callbacks used together.
+ *
+ * Options applicable to ly_type_validate_clb() and ly_typestore_clb.
  * @{
  */
-#define LY_TYPE_VALIDATE_CANONIZE 0x01 /**< Canonize the given value and store it (insert into the context's dictionary)
+#define LY_TYPE_OPTS_VALIDATE     0x01 /**< Flag announcing calling of ly_type_validate_clb() of the type */
+#define LY_TYPE_OPTS_CANONIZE     0x02 /**< Canonize the given value and store it (insert into the context's dictionary)
                                             as the value's canonized string */
-#define LY_TYPE_VALIDATE_DYNAMIC  0x02 /**< Flag for the dynamically allocated string value, in this case the value is supposed to be freed
-                                            or directly used to insert into the node's value structure in case of canonization.
+#define LY_TYPE_OPTS_DYNAMIC      0x04 /**< Flag for the dynamically allocated string value, in this case the value 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 ly_type_validate_clb() with this option */
+                                            the type's callbacks with this option */
+#define LY_TYPE_OPTS_STORE        0x08 /**< Flag announcing calling of ly_type_store_clb() */
+#define LY_TYPE_OPTS_SCHEMA       0x10 /**< Flag for the value used in schema instead of the data tree */
 
 /**
  * @}
@@ -82,15 +86,16 @@
  * @param[in] type Type of the value being canonized.
  * @param[in] value Lexical representation of the value to be validated (and canonized).
  * @param[in] value_len Length of the given \p value.
- * @param[in] options [Type validation options ](@ref plugintypevalidateopts).
+ * @param[in] options [Type plugin options ](@ref plugintypeopts).
  * @param[out] canonized If LY_TYPE_VALIDATE_CANONIZE option set, the canonized string stored in the @p ctx dictionary is returned via this parameter.
  * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
  * The error structure can be created by ly_err_new().
+ * @param[out] priv Type's private data passed between all the callbacks. The last callback is supposed to free the data allocated beforehand.
  * @return LY_SUCCESS on success
  * @return LY_ERR value if an error occurred and the value could not be canonized following the type's rules.
  */
 typedef LY_ERR (*ly_type_validate_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value, size_t value_len, int options,
-                                       const char **canonized, struct ly_err_item **err);
+                                       const char **canonized, struct ly_err_item **err, void **priv);
 
 /**
  * @brief Callback for storing user type values.
@@ -100,14 +105,16 @@
  *
  * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary.
  * @param[in] type Type of the value being stored.
- * @param[in] value_str Canonized string value to be stored.
- * @param[in,out] value Value structure to store the data in the type plugin specific way.
+ * @param[in] options [Type plugin options ](@ref plugintypeopts).
+ * @param[in,out] value Value structure to store the data in the type's specific way. The structure already contains canonized value string to be processed.
  * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
  * The error structure can be created by ly_err_new().
+ * @param[out] priv Type's private data passed between all the callbacks. The last callback is supposed to free the data allocated beforehand.
  * @return LY_SUCCESS on success
  * @return LY_ERR value if an error occurred and the value could not be stored for any reason.
  */
-typedef LY_ERR (*ly_type_store_clb)(struct ly_ctx *ctx, struct lysc_type *type, const char *value_str, struct lyd_value *value, struct ly_err_item **err);
+typedef LY_ERR (*ly_type_store_clb)(struct ly_ctx *ctx, struct lysc_type *type, int options,
+                                    struct lyd_value *value, struct ly_err_item **err, void **priv);
 
 /**
  * @brief Hold type-specific functions for various operations with the data values.
@@ -129,13 +136,46 @@
 extern struct lysc_type_plugin ly_builtin_type_plugins[LY_DATA_TYPE_COUNT];
 
 /**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] min Lower bound of the type.
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed integer value (optional).
+ * @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 parsing and validation.
+ */
+LY_ERR parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len,
+                 int64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the unsigned integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] min Lower bound of the type.
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed unsigned integer value (optional).
+ * @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 parsing and validation.
+ */
+LY_ERR parse_uint(const char *datatype, int base, uint64_t min, uint64_t max, const char *value, size_t value_len,
+                  uint64_t *ret, struct ly_err_item **err);
+
+/**
  * @brief Data type validator for a range/length-restricted values.
  *
- * @param[in] ctx libyang context
+ * @param[in] basetype Base built-in type of the type with the range specified to get know if the @p range structure represents range or length restriction.
  * @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[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic error message is prepared instead.
- * The error structure can be created by ly_err_new().
+ * @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 ly_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, struct ly_err_item **err);
diff --git a/src/tree_data.h b/src/tree_data.h
index 9fe0af6..933d0d2 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -187,7 +187,6 @@
         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 works only with the canonized string,
              this specific data type storage is just to simplify use of the values by the libyang users. */
-    LY_DATA_TYPE type;               /**< type of the value in the node, mainly for union to avoid repeating of type detection */
 };
 
 /**
diff --git a/src/tree_data_helpers.c b/src/tree_data_helpers.c
index fc4bf1c..3f385a2 100644
--- a/src/tree_data_helpers.c
+++ b/src/tree_data_helpers.c
@@ -90,22 +90,35 @@
     struct ly_err_item *err = NULL;
     struct ly_ctx *ctx;
     struct lysc_type *type;
+    void *priv = NULL;
 
     assert(node);
 
     ctx = node->schema->module->ctx;
     type = ((struct lysc_node_leaf*)node->schema)->type;
     if (type->plugin->validate) {
-        ret = type->plugin->validate(ctx, type, value, value_len, options, &node->value.canonized, &err);
+        ret = type->plugin->validate(ctx, type, value, value_len, options, &node->value.canonized, &err, &priv);
         if (ret) {
             ly_err_print(err);
             LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
             ly_err_free(err);
+            goto error;
         }
-    } else if (options & LY_TYPE_VALIDATE_CANONIZE) {
+    } else if (options & LY_TYPE_OPTS_CANONIZE) {
         node->value.canonized = lydict_insert(ctx, value, value_len);
     }
 
+    if ((options & LY_TYPE_OPTS_STORE) && type->plugin->store) {
+        ret = type->plugin->store(ctx, type, options, &node->value, &err, &priv);
+        if (ret) {
+            ly_err_print(err);
+            LOGVAL(ctx, LY_VLOG_STR, err->path, err->vecode, err->msg);
+            ly_err_free(err);
+            goto error;
+        }
+    }
+
+error:
     return ret;
 }
 
diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h
index ba0729e..f6be23c 100644
--- a/src/tree_data_internal.h
+++ b/src/tree_data_internal.h
@@ -42,7 +42,7 @@
  *
  * According to the given options, the value can be also canonized or stored into the node's value structure.
  *
- * @param[in] options [Type validation options ](@ref plugintypevalidateopts).
+ * @param[in] options [Type validation options ](@ref plugintypeopts).
  */
 LY_ERR lyd_value_validate(struct lyd_node_term *node, const char *value, size_t value_len, int options);
 
diff --git a/tests/features/test_types.c b/tests/features/test_types.c
index 1f1119a..80f4cd5 100644
--- a/tests/features/test_types.c
+++ b/tests/features/test_types.c
@@ -58,7 +58,15 @@
     struct state_s *s;
     const char *schema_a = "module types {namespace urn:tests:types;prefix t;yang-version 1.1;"
             "leaf binary {type binary {length 5 {error-message \"This bas64 value must be of length 5.\";}}}"
-            "leaf binary-norestr {type binary;}}";
+            "leaf binary-norestr {type binary;}"
+            "leaf int8 {type int8 {range 10..20;}}"
+            "leaf uint8 {type uint8 {range 150..200;}}"
+            "leaf int16 {type int16 {range -20..-10;}}"
+            "leaf uint16 {type uint16 {range 150..200;}}"
+            "leaf int32 {type int32;}"
+            "leaf uint32 {type uint32;}"
+            "leaf int64 {type int64;}"
+            "leaf uint64 {type uint64;}}";;
 
     s = calloc(1, sizeof *s);
     assert_non_null(s);
@@ -105,6 +113,88 @@
 #endif
 
 static void
+test_int(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_int;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<int8 xmlns=\"urn:tests:types\">\n 15 \t\n  </int8>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("int8", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("15", leaf->value.canonized);
+    assert_int_equal(15, leaf->value.int8);
+    lyd_free_all(tree);
+
+    /* invalid range */
+    data = "<int8 xmlns=\"urn:tests:types\">1</int8>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"1\" does not satisfy the range constraint. /");
+
+    data = "<int16 xmlns=\"urn:tests:types\">100</int16>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"100\" does not satisfy the range constraint. /");
+
+    /* invalid value */
+    data = "<int32 xmlns=\"urn:tests:types\">0x01</int32>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid int32 value 0x01. /");
+
+    data = "<int64 xmlns=\"urn:tests:types\"></int64>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty int64 value. /");
+
+    s->func = NULL;
+}
+
+static void
+test_uint(void **state)
+{
+    struct state_s *s = (struct state_s*)(*state);
+    s->func = test_uint;
+
+    struct lyd_node *tree;
+    struct lyd_node_term *leaf;
+
+    const char *data = "<uint8 xmlns=\"urn:tests:types\">\n 150 \t\n  </uint8>";
+
+    /* valid data */
+    assert_non_null(tree = lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
+    assert_string_equal("uint8", tree->schema->name);
+    leaf = (struct lyd_node_term*)tree;
+    assert_string_equal("150", leaf->value.canonized);
+    assert_int_equal(150, leaf->value.uint8);
+    lyd_free_all(tree);
+
+    /* invalid range */
+    data = "<uint8 xmlns=\"urn:tests:types\">\n 15 \t\n  </uint8>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"15\" does not satisfy the range constraint. /");
+
+    data = "<uint16 xmlns=\"urn:tests:types\">\n 1500 \t\n  </uint16>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Value \"1500\" does not satisfy the range constraint. /");
+
+    /* invalid value */
+    data = "<uint32 xmlns=\"urn:tests:types\">-10</uint32>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid uint32 value -10. /");
+
+    data = "<uint64 xmlns=\"urn:tests:types\"/>";
+    assert_null(lyd_parse_mem(s->ctx, data, LYD_XML, 0));
+    logbuf_assert("Invalid empty uint64 value. /");
+
+    s->func = NULL;
+}
+
+static void
 test_binary(void **state)
 {
     struct state_s *s = (struct state_s*)(*state);
@@ -179,6 +269,8 @@
 int main(void)
 {
     const struct CMUnitTest tests[] = {
+        cmocka_unit_test_setup_teardown(test_int, setup, teardown),
+        cmocka_unit_test_setup_teardown(test_uint, setup, teardown),
         cmocka_unit_test_setup_teardown(test_binary, setup, teardown),
     };