plugins types FEATURE fixed size values

Refs #1552

Signed-off-by: Christian Hopps <chopps@labn.net>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ef4085a..35b8621 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -85,9 +85,6 @@
 
 include_directories(${PROJECT_BINARY_DIR}/src ${PROJECT_SOURCE_DIR}/src)
 
-configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/src/config.h @ONLY)
-configure_file(${PROJECT_SOURCE_DIR}/src/version.h.in ${PROJECT_BINARY_DIR}/src/version.h @ONLY)
-
 # type plugins are separate because they have their documentation generated
 set(type_plugins
     src/plugins_types/binary.c
@@ -210,6 +207,10 @@
 option(ENABLE_COVERAGE "Build code coverage report from tests" OFF)
 option(ENABLE_FUZZ_TARGETS "Build target programs suitable for fuzzing with AFL" OFF)
 
+set(LYD_VALUE_SIZE "24" CACHE STRING "Maximum size in bytes of data node values that do not need to be allocated dynamically, minimum is 8")
+if(NOT LYD_VALUE_SIZE GREATER_EQUAL 8)
+    message(FATAL_ERROR "Data node value size \"${LYD_VALUE_SIZE}\" is not valid.")
+endif()
 set(PLUGINS_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/libyang" CACHE STRING "Directory with libyang plugins (extensions and user types)")
 set(PLUGINS_DIR_EXTENSIONS "${PLUGINS_DIR}/extensions" CACHE STRING "Directory with libyang user extensions plugins")
 set(PLUGINS_DIR_TYPES "${PLUGINS_DIR}/types" CACHE STRING "Directory with libyang user types plugins")
@@ -251,6 +252,10 @@
 # static build requires static libpcre2 library
 option(ENABLE_STATIC "Build static (.a) library" OFF)
 
+# generate files
+configure_file(${PROJECT_SOURCE_DIR}/src/config.h.in ${PROJECT_BINARY_DIR}/src/config.h @ONLY)
+configure_file(${PROJECT_SOURCE_DIR}/src/version.h.in ${PROJECT_BINARY_DIR}/src/version.h @ONLY)
+
 #
 # targets
 #
diff --git a/src/config.h.in b/src/config.h.in
index ab861bd..65c4934 100644
--- a/src/config.h.in
+++ b/src/config.h.in
@@ -15,6 +15,9 @@
 #ifndef LY_CONFIG_H_
 #define LY_CONFIG_H_
 
+/** size of fixed_mem in lyd_value, minimum is 8 (B) */
+#define LYD_VALUE_FIXED_MEM_SIZE @LYD_VALUE_SIZE@
+
 /*
  * Plugins
  */
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 3a1372f..ac23c24 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -301,7 +301,7 @@
 lyplg_type_dup_simple(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));
-    dup->ptr = original->ptr;
+    memcpy(dup->fixed_mem, original->fixed_mem, sizeof dup->fixed_mem);
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 }
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 43e567f..dc7ca13 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -18,6 +18,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "config.h"
 #include "log.h"
 #include "plugins.h"
 #include "tree.h"
@@ -170,6 +171,38 @@
     const struct lyplg_type_record plugins_types__[]
 
 /**
+ * @brief Check whether specific type value needs to be allocated dynamically.
+ *
+ * @param[in] type_val Pointer to specific type value storage.
+ */
+#define LYPLG_TYPE_VAL_IS_DYN(type_val) \
+    (sizeof *(type_val) > LYD_VALUE_FIXED_MEM_SIZE)
+
+/**
+ * @brief Prepare value memory for storing a specific type value, may be allocated dynamically.
+ *
+ * Must be called for values larger than 8 bytes.
+ * To be used in ::lyplg_type_store_clb.
+ *
+ * @param[in] storage Pointer to the value storage to use (struct ::lyd_value *).
+ * @param[in,out] type_val Pointer to specific type value structure.
+ */
+#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val) \
+    (LYPLG_TYPE_VAL_IS_DYN(type_val) \
+     ? ((type_val) = ((storage)->dyn_mem = calloc(1, sizeof *(type_val)))) \
+     : ((type_val) = memset((storage)->fixed_mem, 0, sizeof *(type_val))))
+
+/**
+ * @brief Destroy a prepared value.
+ *
+ * Must be called for values prepared with ::LYPLG_TYPE_VAL_INLINE_PREPARE.
+ *
+ * @param[in] type_val Pointer to specific type value structure.
+ */
+#define LYPLG_TYPE_VAL_INLINE_DESTROY(type_val) \
+    do { if (LYPLG_TYPE_VAL_IS_DYN(type_val)) free(type_val); } while(0)
+
+/**
  * @brief Create and fill error structure.
  *
  * Helper function for various plugin functions to generate error information structure.
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
index aed2c35..7fe387b 100644
--- a/src/plugins_types/binary.c
+++ b/src/plugins_types/binary.c
@@ -215,19 +215,13 @@
     struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
     struct lyd_value_binary *val;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     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;
@@ -255,15 +249,6 @@
         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->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);
@@ -292,12 +277,15 @@
 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;
+    struct lyd_value_binary *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
         return LY_ENOT;
     }
@@ -308,10 +296,12 @@
 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;
+    struct lyd_value_binary *val;
     char *ret;
     size_t ret_len = 0;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         *dynamic = 0;
         if (value_len) {
@@ -348,27 +338,27 @@
 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;
+    struct lyd_value_binary *orig_val, *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);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
     }
 
+    LYD_VALUE_GET(original, orig_val);
     dup_val->data = malloc(orig_val->size);
     if (!dup_val->data) {
         lydict_remove(ctx, dup->_canonical);
-        free(dup_val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(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;
 }
@@ -376,12 +366,13 @@
 API void
 lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_binary *val = value->bin;
+    struct lyd_value_binary *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         free(val->data);
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
index 481d71b..bd76e4d 100644
--- a/src/plugins_types/bits.c
+++ b/src/plugins_types/bits.c
@@ -297,8 +297,11 @@
     struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type;
     struct lyd_value_bits *val;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -308,15 +311,6 @@
             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 value (bitmap) */
         if (options & LYPLG_TYPE_STORE_DYNAMIC) {
             val->bitmap = (char *)value;
@@ -339,15 +333,6 @@
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, 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;
-
     /* allocate the bitmap */
     val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
     LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
@@ -387,13 +372,16 @@
 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 lyd_value_bits *v1, *v2;
     struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     if (memcmp(v1->bitmap, v2->bitmap, lyplg_type_bits_bitmap_size(type_bits))) {
         return LY_ENOT;
     }
@@ -405,9 +393,11 @@
         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;
+    struct lyd_value_bits *val;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         *dynamic = 0;
         if (value_len) {
@@ -446,7 +436,7 @@
     LY_ERR ret;
     struct lysc_type_bits *type_bits = (struct lysc_type_bits *)original->realtype;
     LY_ARRAY_COUNT_TYPE u;
-    struct lyd_value_bits *orig_val = original->ptr, *dup_val;
+    struct lyd_value_bits *orig_val, *dup_val;
 
     memset(dup, 0, sizeof *dup);
 
@@ -455,9 +445,10 @@
     LY_CHECK_GOTO(ret, error);
 
     /* allocate value */
-    dup_val = calloc(1, sizeof *dup_val);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
-    dup->ptr = dup_val;
+
+    LYD_VALUE_GET(original, orig_val);
 
     /* duplicate bitmap */
     dup_val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
@@ -471,7 +462,6 @@
         dup_val->items[u] = orig_val->items[u];
     }
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 
@@ -483,13 +473,14 @@
 API void
 lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_bits *val = value->ptr;
+    struct lyd_value_bits *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         free(val->bitmap);
         LY_ARRAY_FREE(val->items);
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
index 842832f..72358c2 100644
--- a/src/plugins_types/boolean.c
+++ b/src/plugins_types/boolean.c
@@ -44,6 +44,10 @@
     LY_ERR ret = LY_SUCCESS;
     int8_t i;
 
+    /* init storage */
+    memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
+
     if (format == LY_VALUE_LYB) {
         /* validation */
         if (value_len != 1) {
@@ -52,13 +56,9 @@
             goto cleanup;
         }
 
-        /* cast the value */
+        /* store 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);
@@ -72,7 +72,7 @@
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
     LY_CHECK_GOTO(ret, cleanup);
 
-    /* validate and get the value */
+    /* validate and store 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"))) {
@@ -82,11 +82,7 @@
                 (char *)value);
         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) {
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 4554ae2..cbfb809 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -186,8 +186,11 @@
     uint32_t i;
     char c;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -205,15 +208,6 @@
             }
         }
 
-        /* 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 timestamp */
         memcpy(&val->time, value, sizeof val->time);
 
@@ -241,15 +235,6 @@
     ret = lyplg_type_validate_patterns(type_dat->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;
-
     /* 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);
@@ -283,12 +268,15 @@
 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;
+    struct lyd_value_date_and_time *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     /* compare timestamp */
     if (v1->time != v2->time) {
         return LY_ENOT;
@@ -309,9 +297,11 @@
 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;
+    struct lyd_value_date_and_time *val;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         if (val->fractions_s) {
             ret = malloc(8 + strlen(val->fractions_s));
@@ -364,7 +354,7 @@
 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;
+    struct lyd_value_date_and_time *orig_val, *dup_val;
 
     memset(dup, 0, sizeof *dup);
 
@@ -373,9 +363,10 @@
     LY_CHECK_GOTO(ret, error);
 
     /* allocate value */
-    dup_val = calloc(1, sizeof *dup_val);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
-    dup->ptr = dup_val;
+
+    LYD_VALUE_GET(original, orig_val);
 
     /* copy timestamp */
     dup_val->time = orig_val->time;
@@ -386,7 +377,6 @@
         LY_CHECK_ERR_GOTO(!dup_val->fractions_s, ret = LY_EMEM, error);
     }
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 
@@ -401,12 +391,13 @@
 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;
+    struct lyd_value_date_and_time *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         free(val->fractions_s);
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
index 4df6bb9..7c8a6b9 100644
--- a/src/plugins_types/decimal64.c
+++ b/src/plugins_types/decimal64.c
@@ -93,8 +93,9 @@
     int64_t num;
     char *canon;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -116,12 +117,10 @@
         LY_CHECK_GOTO(ret, cleanup);
     }
 
-    /* init storage */
-    storage->_canonical = NULL;
+    /* store value */
     storage->dec64 = num;
-    storage->realtype = type;
 
-    /* we need canonical value for hash */
+    /* we need canonical value for the range check */
     if (format == LY_VALUE_CANON) {
         /* store canonical value */
         if (options & LYPLG_TYPE_STORE_DYNAMIC) {
diff --git a/src/plugins_types/empty.c b/src/plugins_types/empty.c
index c7aa6db..3753923 100644
--- a/src/plugins_types/empty.c
+++ b/src/plugins_types/empty.c
@@ -42,8 +42,9 @@
 {
     LY_ERR ret = LY_SUCCESS;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
@@ -55,11 +56,6 @@
         goto 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);
diff --git a/src/plugins_types/enumeration.c b/src/plugins_types/enumeration.c
index e233943..092bd57 100644
--- a/src/plugins_types/enumeration.c
+++ b/src/plugins_types/enumeration.c
@@ -45,8 +45,9 @@
     LY_ARRAY_COUNT_TYPE u;
     ly_bool found = 0;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -91,10 +92,8 @@
         }
     }
 
-    /* init storage */
-    storage->_canonical = NULL;
+    /* store value */
     storage->enum_item = &type_enum->enums[u];
-    storage->realtype = type;
 
     /* store canonical value */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index e06fce9..c8e655a 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -194,8 +194,9 @@
     char *canon;
     struct lysc_ident *ident;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
@@ -222,10 +223,8 @@
     ret = identityref_check_base(ident, type_ident, value, value_len, err);
     LY_CHECK_GOTO(ret, cleanup);
 
-    /* init storage */
-    storage->_canonical = NULL;
+    /* store value */
     storage->ident = ident;
-    storage->realtype = type;
 
     /* store canonical value */
     if (format == LY_VALUE_CANON) {
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index c8c52f5..7559f8e 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -154,8 +154,9 @@
     struct ly_path *path;
     char *canon;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
@@ -166,10 +167,8 @@
             unres, &path, err);
     LY_CHECK_GOTO(ret, cleanup);
 
-    /* init storage */
-    storage->_canonical = NULL;
+    /* store value */
     storage->target = path;
-    storage->realtype = type;
 
     /* store canonical value */
     if (format == LY_VALUE_CANON) {
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
index 451d3d9..2e63a4e 100644
--- a/src/plugins_types/integer.c
+++ b/src/plugins_types/integer.c
@@ -58,8 +58,9 @@
     char *canon;
     struct lysc_type_num *type_num = (struct lysc_type_num *)type;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -115,9 +116,7 @@
         LY_CHECK_GOTO(ret, cleanup);
     }
 
-    /* init storage */
-    storage->_canonical = NULL;
-    /* matters for big-endian */
+    /* set the value, matters for big-endian */
     switch (type->basetype) {
     case LY_TYPE_INT8:
         storage->int8 = num;
@@ -134,7 +133,6 @@
     default:
         break;
     }
-    storage->realtype = type;
 
     if (format == LY_VALUE_CANON) {
         /* store canonical value */
@@ -253,8 +251,9 @@
     char *canon;
     struct lysc_type_num *type_num = (struct lysc_type_num *)type;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -309,9 +308,7 @@
         LY_CHECK_GOTO(ret, cleanup);
     }
 
-    /* init storage */
-    storage->_canonical = NULL;
-    /* matters for big-endian */
+    /* store value, matters for big-endian */
     switch (type->basetype) {
     case LY_TYPE_UINT8:
         storage->uint8 = num;
@@ -328,7 +325,6 @@
     default:
         break;
     }
-    storage->realtype = type;
 
     if (format == LY_VALUE_CANON) {
         /* store canonical value */
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
index ff7d752..96af2d2 100644
--- a/src/plugins_types/ipv4_address.c
+++ b/src/plugins_types/ipv4_address.c
@@ -135,8 +135,11 @@
     struct lyd_value_ipv4_address *val;
     size_t i;
 
-    /* zero storage so we can always free it */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -153,15 +156,6 @@
             }
         }
 
-        /* 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);
 
@@ -192,15 +186,6 @@
     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);
@@ -232,12 +217,15 @@
 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;
+    struct lyd_value_ipv4_address *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     /* zones are NULL or in the dictionary */
     if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr) || (v1->zone != v2->zone)) {
         return LY_ENOT;
@@ -252,10 +240,12 @@
 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;
+    struct lyd_value_ipv4_address *val;
     size_t zone_len;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         if (!val->zone) {
             /* address-only, const */
@@ -324,25 +314,26 @@
 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;
+    struct lyd_value_ipv4_address *orig_val, *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);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
     }
+
+    LYD_VALUE_GET(original, orig_val);
     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);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(dup_val);
         return ret;
     }
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 }
@@ -353,12 +344,13 @@
 static void
 lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_ipv4_address *val = value->ptr;
+    struct lyd_value_ipv4_address *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         lydict_remove(ctx, val->zone);
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/plugins_types/ipv4_address_no_zone.c b/src/plugins_types/ipv4_address_no_zone.c
index 94330e8..0c99351 100644
--- a/src/plugins_types/ipv4_address_no_zone.c
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -61,9 +61,13 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+    struct lyd_value_ipv4_address_no_zone *val;
 
-    /* zero storage so we can always free it */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -73,13 +77,8 @@
             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);
+        /* store IP address */
+        memcpy(&val->addr, value, 4);
 
         /* success */
         goto cleanup;
@@ -100,11 +99,6 @@
     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);
@@ -114,7 +108,7 @@
     }
 
     /* get the network-byte order address */
-    if (!inet_pton(AF_INET, value, &storage->uint32)) {
+    if (!inet_pton(AF_INET, value, &val->addr)) {
         ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", (char *)value);
         goto cleanup;
     }
@@ -141,11 +135,16 @@
 static LY_ERR
 lyplg_type_compare_ipv4_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
 {
+    struct lyd_value_ipv4_address_no_zone *v1, *v2;
+
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
-    if (val1->uint32 != val2->uint32) {
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
+    if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
         return LY_ENOT;
     }
     return LY_SUCCESS;
@@ -158,14 +157,17 @@
 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)
 {
+    struct lyd_value_ipv4_address_no_zone *val;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         *dynamic = 0;
         if (value_len) {
             *value_len = 4;
         }
-        return &value->uint32;
+        return &val->addr;
     }
 
     /* generate canonical value if not already (loaded from LYB) */
@@ -174,7 +176,7 @@
         LY_CHECK_RET(!ret, NULL);
 
         /* get the address in string */
-        if (!inet_ntop(AF_INET, &value->uint32, ret, INET_ADDRSTRLEN)) {
+        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;
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
index 85dff33..2d66b00 100644
--- a/src/plugins_types/ipv4_prefix.c
+++ b/src/plugins_types/ipv4_prefix.c
@@ -125,8 +125,11 @@
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
     struct lyd_value_ipv4_prefix *val;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -141,20 +144,8 @@
             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;
+        /* store addr + prefix */
+        memcpy(val, value, value_len);
 
         /* zero host */
         ipv4prefix_zero_host(&val->addr, val->prefix);
@@ -178,15 +169,6 @@
     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);
@@ -223,12 +205,15 @@
 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;
+    struct lyd_value_ipv4_prefix *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     if (memcmp(v1, v2, sizeof *v1)) {
         return LY_ENOT;
     }
@@ -242,9 +227,11 @@
 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;
+    struct lyd_value_ipv4_prefix *val;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         *dynamic = 0;
         if (value_len) {
@@ -292,19 +279,20 @@
 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;
+    struct lyd_value_ipv4_prefix *orig_val, *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);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
     }
+
+    LYD_VALUE_GET(original, orig_val);
     memcpy(dup_val, orig_val, sizeof *orig_val);
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 }
@@ -315,8 +303,11 @@
 static void
 lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
 {
+    struct lyd_value_ipv4_prefix *val;
+
     lydict_remove(ctx, value->_canonical);
-    free(value->ptr);
+    LYD_VALUE_GET(value, val);
+    LYPLG_TYPE_VAL_INLINE_DESTROY(val);
 }
 
 /**
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
index 2b43d1d..39450f1f 100644
--- a/src/plugins_types/ipv6_address.c
+++ b/src/plugins_types/ipv6_address.c
@@ -135,8 +135,11 @@
     struct lyd_value_ipv6_address *val;
     size_t i;
 
-    /* zero storage so we can always free it */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -153,15 +156,6 @@
             }
         }
 
-        /* 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);
 
@@ -192,15 +186,6 @@
     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 = ipv6address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
     LY_CHECK_GOTO(ret, cleanup);
@@ -234,12 +219,15 @@
 static LY_ERR
 lyplg_type_compare_ipv6_address(const struct lyd_value *val1, const struct lyd_value *val2)
 {
-    struct lyd_value_ipv6_address *v1 = val1->ptr, *v2 = val2->ptr;
+    struct lyd_value_ipv6_address *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     /* zones are NULL or in the dictionary */
     if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr) || (v1->zone != v2->zone)) {
         return LY_ENOT;
@@ -254,10 +242,12 @@
 lyplg_type_print_ipv6_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_ipv6_address *val = value->ptr;
+    struct lyd_value_ipv6_address *val;
     size_t zone_len;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         if (!val->zone) {
             /* address-only, const */
@@ -326,16 +316,18 @@
 lyplg_type_dup_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
 {
     LY_ERR ret;
-    struct lyd_value_ipv6_address *orig_val = original->ptr, *dup_val;
+    struct lyd_value_ipv6_address *orig_val, *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);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
     }
+
+    LYD_VALUE_GET(original, orig_val);
     memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
     ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
     if (ret) {
@@ -344,7 +336,6 @@
         return ret;
     }
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 }
@@ -355,12 +346,13 @@
 static void
 lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_ipv6_address *val = value->ptr;
+    struct lyd_value_ipv6_address *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         lydict_remove(ctx, val->zone);
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/plugins_types/ipv6_address_no_zone.c b/src/plugins_types/ipv6_address_no_zone.c
index bead9ed..409f381 100644
--- a/src/plugins_types/ipv6_address_no_zone.c
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -102,8 +102,9 @@
     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 */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -113,20 +114,14 @@
             goto cleanup;
         }
 
-        /* init storage */
-        storage->_canonical = NULL;
-        storage->ptr = NULL;
-        storage->realtype = type;
-
-        if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+        if ((options & LYPLG_TYPE_STORE_DYNAMIC) && LYPLG_TYPE_VAL_IS_DYN(val)) {
             /* use the value directly */
-            storage->ptr = (void *)value;
+            storage->dyn_mem = (void *)value;
             options &= ~LYPLG_TYPE_STORE_DYNAMIC;
         } else {
-            /* allocate the value */
-            val = calloc(1, sizeof *val);
+            /* allocate value */
+            LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
             LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
-            storage->ptr = val;
 
             /* store IP address */
             memcpy(&val->addr, value, sizeof val->addr);
@@ -136,6 +131,10 @@
         goto cleanup;
     }
 
+    /* allocate value */
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
     LY_CHECK_GOTO(ret, cleanup);
@@ -151,15 +150,6 @@
     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);
@@ -193,12 +183,15 @@
 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;
+    struct lyd_value_ipv6_address_no_zone *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
         return LY_ENOT;
     }
@@ -212,9 +205,11 @@
 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;
+    struct lyd_value_ipv6_address_no_zone *val;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         *dynamic = 0;
         if (value_len) {
@@ -260,19 +255,20 @@
 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;
+    struct lyd_value_ipv6_address_no_zone *orig_val, *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);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
     }
+
+    LYD_VALUE_GET(original, orig_val);
     memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 }
@@ -283,10 +279,11 @@
 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;
+    struct lyd_value_ipv6_address_no_zone *val;
 
     lydict_remove(ctx, value->_canonical);
-    free(val);
+    LYD_VALUE_GET(value, val);
+    LYPLG_TYPE_VAL_INLINE_DESTROY(val);
 }
 
 /**
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
index ce7767b..22fa2c6 100644
--- a/src/plugins_types/ipv6_prefix.c
+++ b/src/plugins_types/ipv6_prefix.c
@@ -127,8 +127,9 @@
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
     struct lyd_value_ipv6_prefix *val;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* validation */
@@ -145,19 +146,15 @@
 
         /* store/allocate value */
         if (options & LYPLG_TYPE_STORE_DYNAMIC) {
-            val = (void *)value;
+            storage->dyn_mem = (void *)value;
             options &= ~LYPLG_TYPE_STORE_DYNAMIC;
         } else {
-            val = calloc(1, sizeof *val);
+            LYPLG_TYPE_VAL_INLINE_PREPARE(storage, 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);
 
@@ -165,6 +162,10 @@
         goto cleanup;
     }
 
+    /* allocate value */
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
     LY_CHECK_GOTO(ret, cleanup);
@@ -180,15 +181,6 @@
     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);
@@ -225,12 +217,15 @@
 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;
+    struct lyd_value_ipv6_prefix *v1, *v2;
 
     if (val1->realtype != val2->realtype) {
         return LY_ENOT;
     }
 
+    LYD_VALUE_GET(val1, v1);
+    LYD_VALUE_GET(val2, v2);
+
     if (memcmp(v1, v2, sizeof *v1)) {
         return LY_ENOT;
     }
@@ -244,9 +239,11 @@
 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;
+    struct lyd_value_ipv6_prefix *val;
     char *ret;
 
+    LYD_VALUE_GET(value, val);
+
     if (format == LY_VALUE_LYB) {
         *dynamic = 0;
         if (value_len) {
@@ -294,19 +291,20 @@
 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;
+    struct lyd_value_ipv6_prefix *orig_val, *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);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     if (!dup_val) {
         lydict_remove(ctx, dup->_canonical);
         return LY_EMEM;
     }
+
+    LYD_VALUE_GET(original, orig_val);
     memcpy(dup_val, orig_val, sizeof *orig_val);
 
-    dup->ptr = dup_val;
     dup->realtype = original->realtype;
     return LY_SUCCESS;
 }
@@ -317,8 +315,11 @@
 static void
 lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
 {
+    struct lyd_value_ipv6_prefix *val;
+
     lydict_remove(ctx, value->_canonical);
-    free(value->ptr);
+    LYD_VALUE_GET(value, val);
+    LYPLG_TYPE_VAL_INLINE_DESTROY(val);
 }
 
 /**
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
index 0083da7..eede579 100644
--- a/src/plugins_types/string.c
+++ b/src/plugins_types/string.c
@@ -43,8 +43,9 @@
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_str *type_str = (struct lysc_type_str *)type;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    storage->realtype = type;
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
@@ -61,11 +62,6 @@
     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;
-
     /* store canonical value */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
         ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 6bfb6df..cda1e1a 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -144,13 +144,16 @@
 {
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_union *type_u = (struct lysc_type_union *)type;
-    struct lyd_value_union *subvalue = NULL;
+    struct lyd_value_union *subvalue;
     uint32_t type_idx;
 
     *err = NULL;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, subvalue);
+    LY_CHECK_ERR_GOTO(!subvalue, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     if (format == LY_VALUE_LYB) {
         /* basic validation */
@@ -167,18 +170,6 @@
         }
     }
 
-    /* prepare subvalue */
-    subvalue = calloc(1, sizeof *subvalue);
-    if (!subvalue) {
-        ret = ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
-        goto cleanup;
-    }
-
-    /* init storage */
-    storage->_canonical = NULL;
-    storage->subvalue = subvalue;
-    storage->realtype = type;
-
     /* remember the original value */
     if (options & LYPLG_TYPE_STORE_DYNAMIC) {
         subvalue->original = (void *)value;
@@ -339,9 +330,10 @@
 API void
 lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_union *val = value->subvalue;
+    struct lyd_value_union *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         if (val->value.realtype) {
             val->value.realtype->plugin->free(ctx, &val->value);
@@ -349,7 +341,7 @@
         lyplg_type_prefix_data_free(val->format, val->prefix_data);
         free(val->original);
 
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index a743dd0..29059ba 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -43,8 +43,8 @@
 struct lyd_value_xpath10 {
     struct lyxp_expr *exp;
     const struct ly_ctx *ctx;
-    LY_VALUE_FORMAT format;
     void *prefix_data;
+    LY_VALUE_FORMAT format;
 };
 
 /**
@@ -216,8 +216,11 @@
     struct lyd_value_xpath10 *val;
     char *canon;
 
-    /* clear storage */
+    /* init storage */
     memset(storage, 0, sizeof *storage);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+    LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+    storage->realtype = type;
 
     /* check hints */
     ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
@@ -234,16 +237,6 @@
     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, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
     LY_CHECK_GOTO(ret, cleanup);
@@ -287,9 +280,12 @@
 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)
 {
+    struct lyd_value_xpath10 *val;
     char *ret;
     struct ly_err_item *err = NULL;
 
+    LYD_VALUE_GET(value, val);
+
     if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
         /* canonical */
         if (dynamic) {
@@ -302,7 +298,7 @@
     }
 
     /* print in the specific format */
-    if (xpath10_print_value(value->ptr, format, prefix_data, &ret, &err)) {
+    if (xpath10_print_value(val, format, prefix_data, &ret, &err)) {
         if (err) {
             LOGVAL_ERRITEM(ctx, err);
             ly_err_free(err);
@@ -321,7 +317,7 @@
 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 *orig_val = original->ptr, *dup_val;
+    struct lyd_value_xpath10 *orig_val, *dup_val;
 
     /* init dup value */
     memset(dup, 0, sizeof *dup);
@@ -330,17 +326,17 @@
     ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
     LY_CHECK_GOTO(ret, cleanup);
 
-    dup_val = calloc(1, sizeof *dup_val);
+    LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
     LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
     dup_val->ctx = ctx;
-    dup->ptr = dup_val;
 
+    LYD_VALUE_GET(original, orig_val);
     ret = lyxp_expr_dup(ctx, orig_val->exp, &dup_val->exp);
     LY_CHECK_GOTO(ret, cleanup);
 
-    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);
+    dup_val->format = orig_val->format;
 
 cleanup:
     if (ret) {
@@ -352,14 +348,15 @@
 API void
 lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value)
 {
-    struct lyd_value_xpath10 *val = value->ptr;
+    struct lyd_value_xpath10 *val;
 
     lydict_remove(ctx, value->_canonical);
+    LYD_VALUE_GET(value, val);
     if (val) {
         lyxp_expr_free(ctx, val->exp);
         lyplg_type_prefix_data_free(val->format, val->prefix_data);
 
-        free(val);
+        LYPLG_TYPE_VAL_INLINE_DESTROY(val);
     }
 }
 
diff --git a/src/tree_data.h b/src/tree_data.h
index 1fcc38a..0e8ac71 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -18,6 +18,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include "config.h"
 #include "log.h"
 #include "tree.h"
 #include "tree_schema.h"
@@ -522,6 +523,13 @@
     const char *_canonical;          /**< Should never be accessed directly, instead ::lyd_get_value() and ::lyd_get_meta_value()
                                           should be used. Serves as a cache for the canonical value or the JSON
                                           representation if no canonical value is defined. */
+    const struct lysc_type *realtype; /**< pointer to the real type of the data stored in the value structure. This type can differ from the type
+                                          in the schema node of the data node since the type's store plugin can use other types/plugins for
+                                          storing data. Speaking about built-in types, this is the case of leafref which stores data as its
+                                          target type. In contrast, union type also uses its subtype's callbacks, but inside an internal data
+                                          stored in subvalue member of ::lyd_value structure, so here is the pointer to the union type.
+                                          In general, this type is used to get free callback for this lyd_value structure, so it must reflect
+                                          the type used to store data directly in the same lyd_value instance. */
 
     union {
         int8_t boolean;              /**< 0 as false, 1 as true */
@@ -535,25 +543,30 @@
         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 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_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 */
+
+        void *dyn_mem;               /**< pointer to generic data type value stored in dynamic memory */
+        uint8_t fixed_mem[LYD_VALUE_FIXED_MEM_SIZE]; /**< fixed-size buffer for a generic data type value */
     };  /**< 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.*/
-
-    const struct lysc_type *realtype; /**< pointer to the real type of the data stored in the value structure. This type can differ from the type
-                                          in the schema node of the data node since the type's store plugin can use other types/plugins for
-                                          storing data. Speaking about built-in types, this is the case of leafref which stores data as its
-                                          target type. In contrast, union type also uses its subtype's callbacks, but inside an internal data
-                                          stored in subvalue member of ::lyd_value structure, so here is the pointer to the union type.
-                                          In general, this type is used to get free callback for this lyd_value structure, so it must reflect
-                                          the type used to store data directly in the same lyd_value instance. */
 };
 
 /**
+ * @brief Get the value in format specific to the type.
+ *
+ * Should be used for any types that do not have their specific representation in the ::lyd_value union.
+ *
+ * @param[in] value Pointer to the value structure to read from (struct ::lyd_value *).
+ * @param[out] type_val Pointer to the type-specific value structure.
+ */
+#define LYD_VALUE_GET(value, type_val) \
+    ((sizeof *(type_val) > LYD_VALUE_FIXED_MEM_SIZE) \
+     ? ((type_val) = (((value)->dyn_mem))) \
+     : ((type_val) = ((void *)((value)->fixed_mem))))
+
+/**
  * @brief Special lyd_value structure for union.
  *
  * Represents data with multiple types (union). The ::lyd_value_union.value contains representation according to
diff --git a/tests/utests/utests.h b/tests/utests/utests.h
index 76f782c..5bea045 100644
--- a/tests/utests/utests.h
+++ b/tests/utests/utests.h
@@ -932,9 +932,11 @@
     { \
         const char *arr[] = { __VA_ARGS__ }; \
         LY_ARRAY_COUNT_TYPE arr_size = sizeof(arr) / sizeof(arr[0]); \
-        assert_int_equal(arr_size, LY_ARRAY_COUNT(((struct lyd_value_bits *)(NODE).ptr)->items)); \
+        struct lyd_value_bits *_val; \
+        LYD_VALUE_GET(&(NODE), _val); \
+        assert_int_equal(arr_size, LY_ARRAY_COUNT(_val->items)); \
         for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \
-            assert_string_equal(arr[it], ((struct lyd_value_bits *)(NODE).ptr)->items[it]->name); \
+            assert_string_equal(arr[it], _val->items[it]->name); \
         } \
     }
 
@@ -1076,12 +1078,16 @@
  * @param[in] SIZE           expected value data size
 */
 #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); \
-    assert_int_equal(LY_TYPE_BINARY, (NODE).realtype->basetype);
+    { \
+        struct lyd_value_binary *_val; \
+        LYD_VALUE_GET(&(NODE), _val); \
+        assert_int_equal(_val->size, SIZE); \
+        assert_int_equal(0, memcmp(_val->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); \
+        assert_int_equal(LY_TYPE_BINARY, (NODE).realtype->basetype); \
+    }
 
 /**
  * @brief Internal macro. Assert that lyd_value structure members are correct. Lyd value is type BOOL.