plugin union BUGFIX store and parse LYB data
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index ee88c77..85b574f 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -207,14 +207,15 @@
  * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
  * @param[in] ctx_node Context node for prefix resolution.
  * @param[in] tree Data tree for resolving (validation).
+ * @param[out] type_idx Index of the type in which the value was stored.
  * @param[in,out] unres Global unres structure.
  * @param[out] err Error information on error.
  * @return LY_ERR value.
  */
 static LY_ERR
 union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
-        ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
-        struct ly_err_item **err)
+        ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, uint32_t *type_idx,
+        struct lys_glob_unres *unres, struct ly_err_item **err)
 {
     LY_ERR ret = LY_SUCCESS;
     LY_ARRAY_COUNT_TYPE u;
@@ -243,6 +244,8 @@
     if (u == LY_ARRAY_COUNT(types)) {
         ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid union value \"%.*s\" - no matching subtype found.",
                 (int)subvalue->orig_len, (char *)subvalue->original);
+    } else if (type_idx) {
+        *type_idx = u;
     }
 
     /* restore logging */
@@ -250,6 +253,66 @@
     return ret;
 }
 
+/**
+ * @brief Fill union subvalue items: original, origin_len, format
+ * prefix_data and call 'store' function for value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type_u Compiled type of union.
+ * @param[in] lyb_data Input LYB data consisting of index followed by
+ * value (lyb_value).
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[in] prefix_data Format-specific data for resolving any
+ * prefixes (see ly_resolve_prefix()).
+ * @param[in,out] subvalue Union subvalue to be filled.
+ * @param[in,out] options Option containing LYPLG_TYPE_STORE_DYNAMIC.
+ * @param[in,out] unres Global unres structure for newly implemented
+ * modules.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u,
+        const void *lyb_data, size_t lyb_data_len, void *prefix_data,
+        struct lyd_value_union *subvalue, uint32_t *options,
+        struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+    LY_ERR ret;
+    uint32_t type_idx;
+    const void *lyb_value;
+    size_t lyb_value_len;
+
+    ret = lyb_union_validate(lyb_data, lyb_data_len, type_u, err);
+    LY_CHECK_RET(ret);
+
+    /* Parse lyb_data and set the lyb_value and lyb_value_len. */
+    lyb_parse_union(lyb_data, lyb_data_len, &type_idx, &lyb_value, &lyb_value_len);
+    LY_CHECK_RET(ret);
+
+    /* Store lyb_data to subvalue. */
+    ret = union_subvalue_assignment(lyb_data, lyb_data_len,
+            &subvalue->original, &subvalue->orig_len, options);
+    LY_CHECK_RET(ret);
+
+    if (lyb_value) {
+        /* Resolve prefix_data and set format. */
+        ret = lyplg_type_prefix_data_new(ctx, lyb_value, lyb_value_len,
+                LY_VALUE_LYB, prefix_data, &subvalue->format, &subvalue->prefix_data);
+        LY_CHECK_RET(ret);
+        assert(subvalue->format == LY_VALUE_LYB);
+    } else {
+        /* The lyb_parse_union() did not find lyb_value.
+         * Just set format.
+         */
+        subvalue->format = LY_VALUE_LYB;
+    }
+
+    /* Use the specific type to store the value. */
+    ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
+
+    return ret;
+}
+
 API LY_ERR
 lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
         uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -258,9 +321,6 @@
     LY_ERR ret = LY_SUCCESS;
     struct lysc_type_union *type_u = (struct lysc_type_union *)type;
     struct lyd_value_union *subvalue;
-    uint32_t type_idx = 0;
-    const void *lyb_value;
-    size_t lyb_value_len;
 
     *err = NULL;
 
@@ -269,32 +329,26 @@
     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 */
-        lyb_parse_union(value, value_len, &type_idx, &lyb_value, &lyb_value_len);
-        ret = lyb_union_validate(value, value_len, type_u, err);
-        LY_CHECK_GOTO(ret, cleanup);
-    }
-
-    /* remember the original value */
-    ret = union_subvalue_assignment(value, value_len, &subvalue->original, &subvalue->orig_len, &options);
-    LY_CHECK_GOTO(ret, cleanup);
-
-    /* store format-specific data for later prefix resolution */
-    ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &subvalue->format,
-            &subvalue->prefix_data);
-    LY_CHECK_GOTO(ret, cleanup);
     subvalue->hints = hints;
     subvalue->ctx_node = ctx_node;
 
     if (format == LY_VALUE_LYB) {
-        /* use the specific type to store the value */
-        ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
+        ret = lyb_fill_subvalue(ctx, type_u, value, value_len,
+                prefix_data, subvalue, &options, unres, err);
         LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
     } else {
+        /* Store @p value to subvalue. */
+        ret = union_subvalue_assignment(value, value_len,
+                &subvalue->original, &subvalue->orig_len, &options);
+        LY_CHECK_GOTO(ret, cleanup);
+
+        /* store format-specific data for later prefix resolution */
+        ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &subvalue->format,
+                &subvalue->prefix_data);
+        LY_CHECK_GOTO(ret, cleanup);
+
         /* use the first usable subtype to store the value */
-        ret = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, unres, err);
+        ret = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, NULL, unres, err);
         LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
     }
 
@@ -349,12 +403,12 @@
         LY_CHECK_RET(ret);
     } else {
         /* use the first usable subtype to store the value */
-        ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, err);
+        ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
         LY_CHECK_RET(ret);
     }
 
     /* store and resolve the value */
-    ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, err);
+    ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
     LY_CHECK_RET(ret);
 
     /* success, update the canonical value, if any generated */
@@ -376,13 +430,90 @@
     return val1->subvalue->value.realtype->plugin->compare(&val1->subvalue->value, &val2->subvalue->value);
 }
 
+/**
+ * @brief Create LYB data for printing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type_u Compiled type of union.
+ * @param[in] subvalue Union value.
+ * @param[in] prefix_data Format-specific data for resolving any
+ * prefixes (see ly_resolve_prefix()).
+ * @param[out] value_len Length of returned data.
+ * @return Pointer to created LYB data. Caller must release.
+ * @return NULL in case of error.
+ */
+static const void *
+lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u,
+        struct lyd_value_union *subvalue, void *prefix_data, size_t *value_len)
+{
+    void *ret = NULL;
+    LY_ERR retval;
+    struct ly_err_item *err;
+    uint32_t type_idx;
+    ly_bool dynamic = 0;
+    size_t pval_len;
+    void *pval;
+
+    /* Find out the index number (type_idx). The call should succeed
+     * because the union_find_type() has already been called in the
+     * lyplg_type_store_union().
+     */
+    if (!ctx) {
+        assert(subvalue->ctx_node);
+        ctx = subvalue->ctx_node->module->ctx;
+    }
+    subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
+    retval = union_find_type(ctx, type_u->types, subvalue,
+            0, NULL, NULL, &type_idx, NULL, &err);
+    LY_CHECK_GOTO((retval != LY_SUCCESS) && (retval != LY_EINCOMPLETE), cleanup);
+
+    /* Print subvalue in LYB format. */
+    pval = (void *)subvalue->value.realtype->plugin->print(NULL,
+            &subvalue->value, LY_VALUE_LYB, prefix_data, &dynamic,
+            &pval_len);
+
+    /* Create LYB data. */
+    *value_len = IDX_SIZE + pval_len;
+    ret = malloc(*value_len);
+    LY_CHECK_GOTO(!ret, cleanup);
+    memcpy(ret, &type_idx, IDX_SIZE);
+    memcpy(ret + IDX_SIZE, pval, pval_len);
+
+cleanup:
+    if (dynamic) {
+        free(pval);
+    }
+
+    return ret;
+}
+
 API const void *
 lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
         void *prefix_data, ly_bool *dynamic, size_t *value_len)
 {
     const void *ret;
     struct lyd_value_union *subvalue = value->subvalue;
+    struct lysc_type_union *type_u = (struct lysc_type_union *)value->realtype;
+    size_t lyb_data_len;
 
+    if ((format == LY_VALUE_LYB) && (subvalue->format == LY_VALUE_LYB)) {
+        /* The return value is already ready. */
+        *dynamic = 0;
+        if (value_len) {
+            *value_len = subvalue->orig_len;
+        }
+        return subvalue->original;
+    } else if ((format == LY_VALUE_LYB) && (subvalue->format != LY_VALUE_LYB)) {
+        /* The return LYB data must be created. */
+        *dynamic = 1;
+        ret = lyb_union_print(ctx, type_u, subvalue, prefix_data, &lyb_data_len);
+        if (value_len) {
+            *value_len = lyb_data_len;
+        }
+        return ret;
+    }
+
+    assert(format != LY_VALUE_LYB);
     ret = (void *)subvalue->value.realtype->plugin->print(ctx,
             &subvalue->value, format, prefix_data, dynamic, value_len);
     if (!value->_canonical && (format == LY_VALUE_CANON)) {
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index e055745..931b759 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -22,6 +22,7 @@
 ly_add_utest(NAME yang_types   SOURCES types/yang_types.c)
 ly_add_utest(NAME enumeration  SOURCES types/enumeration.c)
 ly_add_utest(NAME instanceid   SOURCES types/instanceid.c)
+ly_add_utest(NAME union        SOURCES types/union.c)
 
 ly_add_utest(NAME range  SOURCES restriction/test_range.c)
 ly_add_utest(NAME pattern  SOURCES restriction/test_pattern.c)
diff --git a/tests/utests/types/union.c b/tests/utests/types/union.c
new file mode 100644
index 0000000..c329970
--- /dev/null
+++ b/tests/utests/types/union.c
@@ -0,0 +1,73 @@
+/**
+ * @file union.c
+ * @author Adam Piecek <piecek@cesnet.cz>
+ * @brief test for built-in enumeration type
+ *
+ * Copyright (c) 2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+/* INCLUDE UTEST HEADER */
+#define  _UTEST_MAIN_
+#include "../utests.h"
+
+/* LOCAL INCLUDE HEADERS */
+#include "libyang.h"
+
+#define MODULE_CREATE_YANG(MOD_NAME, NODES) \
+    "module " MOD_NAME " {\n" \
+    "  yang-version 1.1;\n" \
+    "  namespace \"urn:tests:" MOD_NAME "\";\n" \
+    "  prefix pref;\n" \
+    NODES \
+    "}\n"
+
+#define TEST_SUCCESS_LYB(MOD_NAME, NODE_NAME, DATA) \
+    { \
+        struct lyd_node *tree_1; \
+        struct lyd_node *tree_2; \
+        char *xml_out, *data; \
+        data = "<" NODE_NAME " xmlns=\"urn:tests:" MOD_NAME "\">" DATA "</" NODE_NAME ">"; \
+        CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, LY_SUCCESS, tree_1); \
+        assert_int_equal(lyd_print_mem(&xml_out, tree_1, LYD_LYB, LYD_PRINT_WITHSIBLINGS), 0); \
+        assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(UTEST_LYCTX, xml_out, LYD_LYB, LYD_PARSE_ONLY | LYD_PARSE_STRICT, 0, &tree_2)); \
+        assert_non_null(tree_2); \
+        CHECK_LYD(tree_1, tree_2); \
+        free(xml_out); \
+        lyd_free_all(tree_1); \
+        lyd_free_all(tree_2); \
+    }
+
+static void
+test_plugin_lyb(void **UNUSED(state))
+{
+
+#if 0
+    const char *schema;
+
+    schema = MODULE_CREATE_YANG("lyb",
+            "leaf int8 {type int8 {range 10..20;}}"
+            "leaf un1 {type union {"
+            "    type leafref {path /int8; require-instance true;}"
+            "    type string;}}");
+    UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
+    TEST_SUCCESS_LYB("lyb", "un1", "12");
+    TEST_SUCCESS_LYB("lyb", "un1", "some_string");
+    TEST_SUCCESS_LYB("lyb", "un1", "");
+#endif
+}
+
+int
+main(void)
+{
+    const struct CMUnitTest tests[] = {
+        UTEST(test_plugin_lyb),
+    };
+
+    return cmocka_run_group_tests(tests, NULL, NULL);
+}