plugins types FEATURE union LYB value support
diff --git a/src/plugins_types.h b/src/plugins_types.h
index 191faf5..6d1e9e9 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -904,48 +904,41 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in union type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in union type.
*/
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,
struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
/**
- * @brief Comparison callback checking the union value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in union type.
*/
LY_ERR lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2);
/**
- * @brief Printer callback printing the union value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in union type.
*/
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);
/**
- * @brief Hash key callback for a union value.
- * Implementation of ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for the built-in union type.
*/
const void *lyplg_type_hash_union(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
/**
- * @brief Duplication callback of the union values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in union type.
*/
LY_ERR lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
/**
- * @brief Validate value of the YANG built-in union type.
- * Implementation of the ::lyplg_type_validate_clb.
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in union type.
*/
LY_ERR lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err);
/**
- * @brief Free value of the YANG built-in union types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in union type.
*/
void lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value);
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 4223eb6..54291ed 100644
--- a/src/plugins_types/union.c
+++ b/src/plugins_types/union.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <stdint.h>
@@ -28,8 +26,80 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesUnion union (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `uint32_t *` | index of the resolved type in ::lysc_type_union.types |
+ * | exact same format as the resolved type ||||
+ *
+ * Note that loading union value in this format prevents it from changing its real (resolved) type.
+ */
+
+/**
+ * @brief Store subvalue as a specific type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type Specific union type to use for storing.
+ * @param[in] subvalue Union subvalue structure.
+ * @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[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
static LY_ERR
-ly_type_union_store_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_subvalue *subvalue,
+union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, 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_ERR ret;
+ void *value;
+ size_t value_len;
+
+ if (subvalue->format == LY_VALUE_LYB) {
+ /* skip the type index */
+ value = ((char *)subvalue->original) + 4;
+ value_len = subvalue->orig_len - 4;
+ } else {
+ value = subvalue->original;
+ value_len = subvalue->orig_len;
+ }
+
+ ret = type->plugin->store(ctx, type, value, value_len, 0, subvalue->format, subvalue->prefix_data, subvalue->hints,
+ subvalue->ctx_node, &subvalue->value, unres, err);
+ LY_CHECK_RET((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), ret);
+
+ if (resolve && (ret == LY_EINCOMPLETE)) {
+ /* we need the value resolved */
+ ret = subvalue->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
+ if (ret) {
+ /* resolve failed, we need to free the stored value */
+ type->plugin->free(ctx, &subvalue->value);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Find the first valid type for a union value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] types Sized array of union types.
+ * @param[in] subvalue Union subvalue structure.
+ * @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[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)
{
@@ -48,31 +118,18 @@
/* use the first usable subtype to store the value */
for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
- ret = types[u]->plugin->store(ctx, types[u], subvalue->original, strlen(subvalue->original), 0, subvalue->format,
- subvalue->prefix_data, subvalue->hints, subvalue->ctx_node, &subvalue->value, unres, err);
+ ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err);
if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
- if (resolve && (ret == LY_EINCOMPLETE)) {
- /* we need the value resolved */
- ret = subvalue->value.realtype->plugin->validate(ctx, types[u], ctx_node, tree, &subvalue->value, err);
- if (!ret) {
- /* store and resolve successful */
- break;
- }
-
- /* resolve failed, we need to free the stored value */
- types[u]->plugin->free(ctx, &subvalue->value);
- } else {
- /* store successful */
- break;
- }
+ break;
}
+
ly_err_free(*err);
*err = NULL;
}
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.", subvalue->original);
+ 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);
}
/* restore logging */
@@ -87,56 +144,82 @@
{
LY_ERR ret = LY_SUCCESS;
struct lysc_type_union *type_u = (struct lysc_type_union *)type;
- struct lyd_value_subvalue *subvalue = NULL;
+ struct lyd_value_union *subvalue = NULL;
+ uint32_t type_idx;
*err = NULL;
- /* prepare subvalue storage */
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* basic validation */
+ if (value_len < 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union value size %zu (expected at least 4).",
+ value_len);
+ goto cleanup;
+ }
+ type_idx = *(uint32_t *)value;
+ if (type_idx >= LY_ARRAY_COUNT(type_u->types)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union type index %" PRIu32
+ " (type count " LY_PRI_ARRAY_COUNT_TYPE ").", type_idx, LY_ARRAY_COUNT(type_u->types));
+ goto cleanup;
+ }
+ }
+
+ /* prepare subvalue */
subvalue = calloc(1, sizeof *subvalue);
if (!subvalue) {
ret = ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
- goto cleanup_value;
+ goto cleanup;
}
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->subvalue = subvalue;
+ storage->realtype = type;
+
/* remember the original value */
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- ret = lydict_insert_zc(ctx, (char *)value, &subvalue->original);
+ subvalue->original = (void *)value;
+ subvalue->orig_len = value_len;
options &= ~LYPLG_TYPE_STORE_DYNAMIC;
- LY_CHECK_GOTO(ret, cleanup);
} else {
- ret = lydict_insert(ctx, value_len ? value : "", value_len, &subvalue->original);
- LY_CHECK_GOTO(ret, cleanup);
+ subvalue->original = malloc(value_len);
+ LY_CHECK_ERR_GOTO(!subvalue->original, ret = LY_EMEM, cleanup);
+ memcpy(subvalue->original, value, value_len);
+ subvalue->orig_len = value_len;
}
/* store format-specific data for later prefix resolution */
- ret = lyplg_type_prefix_data_new(ctx, subvalue->original, value_len, format, prefix_data, &subvalue->format,
+ 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;
- /* use the first usable subtype to store the value */
- ret = ly_type_union_store_type(ctx, type_u->types, subvalue, 0, NULL, NULL, unres, err);
- LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ 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);
+ LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ } else {
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, unres, err);
+ LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ }
+
+ /* store canonical value, if any (use the specific type value) */
+ ret = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
cleanup:
- if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
- lydict_remove(ctx, subvalue->original);
- lyplg_type_prefix_data_free(subvalue->format, subvalue->prefix_data);
- free(subvalue);
- } else {
- /* store it as union, the specific type is in the subvalue, but canonical value is the specific type value */
- ret = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical);
- LY_CHECK_GOTO(ret, cleanup);
- storage->subvalue = subvalue;
- storage->realtype = type;
- }
-
-cleanup_value:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ free((void *)value);
}
+ if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+ lyplg_type_free_union(ctx, storage);
+ }
return ret;
}
@@ -146,17 +229,17 @@
{
LY_ERR ret = LY_SUCCESS;
struct lysc_type_union *type_u = (struct lysc_type_union *)storage->realtype;
- struct lyd_value_subvalue *subvalue = storage->subvalue;
+ struct lyd_value_union *val = storage->subvalue;
*err = NULL;
- if (!subvalue->value.realtype->plugin->validate) {
+ if (!val->value.realtype->plugin->validate) {
/* nothing to resolve */
return LY_SUCCESS;
}
/* resolve the stored value */
- if (!subvalue->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err)) {
+ if (!val->value.realtype->plugin->validate(ctx, type, ctx_node, tree, &val->value, err)) {
/* resolve successful */
return LY_SUCCESS;
}
@@ -168,13 +251,24 @@
ly_err_free(*err);
*err = NULL;
+ if (val->format == LY_VALUE_LYB) {
+ /* use the specific type to store the value */
+ uint32_t type_idx = *(uint32_t *)val->original;
+ ret = union_store_type(ctx, type_u->types[type_idx], val, 1, ctx_node, tree, NULL, err);
+ LY_CHECK_RET(ret);
+ } else {
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, val, 1, ctx_node, tree, NULL, err);
+ LY_CHECK_RET(ret);
+ }
+
/* store and resolve the value */
- ret = ly_type_union_store_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, err);
+ ret = union_find_type(ctx, type_u->types, val, 1, ctx_node, tree, NULL, err);
LY_CHECK_RET(ret);
- /* success, update the canonical value */
+ /* success, update the canonical value, if any generated */
lydict_remove(ctx, storage->_canonical);
- LY_CHECK_RET(lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical));
+ LY_CHECK_RET(lydict_insert(ctx, val->value._canonical, 0, &storage->_canonical));
return LY_SUCCESS;
}
@@ -215,34 +309,53 @@
API LY_ERR
lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
- LY_CHECK_RET(lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical));
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_value_union *orig_val = original->subvalue, *dup_val;
- dup->subvalue = calloc(1, sizeof *dup->subvalue);
- LY_CHECK_ERR_RET(!dup->subvalue, LOGMEM(ctx), LY_EMEM);
- LY_CHECK_RET(original->subvalue->value.realtype->plugin->duplicate(ctx, &original->subvalue->value, &dup->subvalue->value));
-
- LY_CHECK_RET(lydict_insert(ctx, original->subvalue->original, strlen(original->subvalue->original),
- &dup->subvalue->original));
- dup->subvalue->format = original->subvalue->format;
- LY_CHECK_RET(lyplg_type_prefix_data_dup(ctx, original->subvalue->format, original->subvalue->prefix_data,
- &dup->subvalue->prefix_data));
-
+ /* init dup value */
+ memset(dup, 0, sizeof *dup);
dup->realtype = original->realtype;
- return LY_SUCCESS;
+
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ dup->subvalue = dup_val;
+
+ ret = orig_val->value.realtype->plugin->duplicate(ctx, &orig_val->value, &dup_val->value);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ dup_val->original = malloc(orig_val->orig_len);
+ LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup_val->original, orig_val->original, orig_val->orig_len);
+ dup_val->orig_len = orig_val->orig_len;
+
+ 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);
+
+cleanup:
+ if (ret) {
+ lyplg_type_free_union(ctx, dup);
+ }
+ return ret;
}
API void
lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value)
{
+ struct lyd_value_union *val = value->subvalue;
+
lydict_remove(ctx, value->_canonical);
- if (value->subvalue) {
- if (value->subvalue->value.realtype) {
- value->subvalue->value.realtype->plugin->free(ctx, &value->subvalue->value);
+ if (val) {
+ if (val->value.realtype) {
+ val->value.realtype->plugin->free(ctx, &val->value);
}
- lyplg_type_prefix_data_free(value->subvalue->format, value->subvalue->prefix_data);
- lydict_remove(ctx, value->subvalue->original);
- free(value->subvalue);
- value->subvalue = NULL;
+ lyplg_type_prefix_data_free(val->format, val->prefix_data);
+ free(val->original);
+
+ free(val);
}
}
diff --git a/src/tree_data.h b/src/tree_data.h
index 0392e44..1fcc38a 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -538,7 +538,7 @@
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_subvalue *subvalue; /** Union value with some metadata. */
+ 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 */
}; /**< The union is just a list of shorthands to possible values stored by a type's plugin. libyang itself uses the ::lyd_value.realtype
@@ -556,14 +556,15 @@
/**
* @brief Special lyd_value structure for union.
*
- * Represents data with multiple types (union). The ::lyd_value_subvalue.value contains representation according to
- * one of the union's types. The ::lyd_value_subvalue.prefix_data provides (possible) mappings from prefixes in
+ * Represents data with multiple types (union). The ::lyd_value_union.value contains representation according to
+ * one of the union's types. The ::lyd_value_union.prefix_data provides (possible) mappings from prefixes in
* the original value to YANG modules. These prefixes are necessary to parse original value to the union's subtypes.
*/
-struct lyd_value_subvalue {
+struct lyd_value_union {
struct lyd_value value; /**< representation of the value according to the selected union's subtype
- (stored as ::lyd_value.realtype here, in subvalue structure */
- const char *original; /**< Original value in the dictionary. */
+ (stored as ::lyd_value.realtype here) */
+ void *original; /**< Original value. */
+ size_t orig_len; /**< Original value length. */
LY_VALUE_FORMAT format; /**< Prefix format of the value. However, this information is also used to decide
whether a value is valid for the specific format or not on later validations
(instance-identifier in XML looks different than in JSON). */