diff options
Diffstat (limited to 'src/plugins_types/union.c')
-rw-r--r-- | src/plugins_types/union.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c new file mode 100644 index 0000000..6e31d1e --- /dev/null +++ b/src/plugins_types/union.c @@ -0,0 +1,585 @@ +/** + * @file union.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief Built-in union type plugin. + * + * Copyright (c) 2019-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 + */ + +#define _GNU_SOURCE /* strdup */ + +#include "plugins_types.h" + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "libyang.h" + +/* additional internal headers for some useful simple macros */ +#include "common.h" +#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 *` | little-endian 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 Size in bytes of the index in the LYB Binary Format. + */ +#define IDX_SIZE 4 + +/** + * @brief Assign a value to the union subvalue. + * + * @param[in] value Value for assignment. + * @param[in] value_len Length of the @p value. + * @param[out] original Destination item of the subvalue. + * @param[out] orig_len Length of the @p original. + * @param[in,out] options Flag containing LYPLG_TYPE_STORE_DYNAMIC. + * @return LY_ERR value. + */ +static LY_ERR +union_subvalue_assignment(const void *value, size_t value_len, void **original, size_t *orig_len, uint32_t *options) +{ + LY_ERR ret = LY_SUCCESS; + + if (*options & LYPLG_TYPE_STORE_DYNAMIC) { + /* The allocated value is stored and spend. */ + *original = (void *)value; + *options &= ~LYPLG_TYPE_STORE_DYNAMIC; + } else if (value_len) { + /* Make copy of the value. */ + *original = calloc(1, value_len); + LY_CHECK_ERR_RET(!*original, ret = LY_EMEM, ret); + memcpy(*original, value, value_len); + } else { + /* Empty value. */ + *original = strdup(""); + LY_CHECK_ERR_RET(!*original, ret = LY_EMEM, ret); + } + *orig_len = value_len; + + return ret; +} + +/** + * @brief Validate LYB Binary Format. + * + * @param[in] lyb_data Source of LYB data to parse. + * @param[in] lyb_data_len Length of @p lyb_data. + * @param[in] type_u Compiled type of union. + * @param[out] err Error information on error. + * @return LY_ERR value. + */ +static LY_ERR +lyb_union_validate(const void *lyb_data, size_t lyb_data_len, const struct lysc_type_union *type_u, struct ly_err_item **err) +{ + LY_ERR ret = LY_SUCCESS; + uint64_t type_idx = 0; + + /* Basic validation. */ + if (lyb_data_len < IDX_SIZE) { + ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union value size %zu (expected at least 4).", + lyb_data_len); + return ret; + } + + /* Get index in correct byte order. */ + memcpy(&type_idx, lyb_data, IDX_SIZE); + type_idx = le64toh(type_idx); + 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 %" PRIu64 " (type count %" LY_PRI_ARRAY_COUNT_TYPE ").", + type_idx, LY_ARRAY_COUNT(type_u->types)); + return ret; + } + + return ret; +} + +/** + * @brief Parse index and lyb_value from LYB Binary Format. + * + * @param[in] lyb_data Source of LYB data to parse. + * @param[in] lyb_data_len Length of @p lyb_data. + * @param[out] type_idx Index of the union type. + * @param[out] lyb_value Value after index number. If there is no value + * after the index, it is set to empty string (""). + * @param[out] lyb_value_len Length of @p lyb_value. + */ +static void +lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, const void **lyb_value, size_t *lyb_value_len) +{ + uint64_t num = 0; + + assert(lyb_data && !(lyb_value && !lyb_value_len)); + + if (type_idx) { + memcpy(&num, lyb_data, IDX_SIZE); + num = le64toh(num); + + *type_idx = num; + } + + if (lyb_value && lyb_value_len && lyb_data_len) { + /* Get lyb_value and its length. */ + if (lyb_data_len == IDX_SIZE) { + *lyb_value_len = 0; + *lyb_value = ""; + } else { + *lyb_value_len = lyb_data_len - IDX_SIZE; + *lyb_value = (char *)lyb_data + IDX_SIZE; + } + } +} + +/** + * @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 +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; + const void *value = NULL; + size_t value_len = 0; + + if (subvalue->format == LY_VALUE_LYB) { + lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len); + } 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); + if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) { + /* clear any leftover/freed garbage */ + memset(&subvalue->value, 0, sizeof subvalue->value); + return ret; + } + + if (resolve && (ret == LY_EINCOMPLETE)) { + /* we need the value resolved */ + ret = type->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[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, uint32_t *type_idx, + struct lys_glob_unres *unres, struct ly_err_item **err) +{ + LY_ERR ret = LY_SUCCESS; + LY_ARRAY_COUNT_TYPE u; + uint32_t temp_lo = 0; + + if (!types || !LY_ARRAY_COUNT(types)) { + return LY_EINVAL; + } + + *err = NULL; + + /* turn logging temporarily off */ + ly_temp_log_options(&temp_lo); + + /* use the first usable subtype to store the value */ + for (u = 0; u < LY_ARRAY_COUNT(types); ++u) { + ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err); + if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) { + 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.", + (int)subvalue->orig_len, (char *)subvalue->original); + } else if (type_idx) { + *type_idx = u; + } + + /* restore logging */ + ly_temp_log_options(NULL); + 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 = NULL; + size_t lyb_value_len = 0; + + 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; +} + +LIBYANG_API_DEF 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) +{ + LY_ERR ret = LY_SUCCESS, r; + struct lysc_type_union *type_u = (struct lysc_type_union *)type; + struct lyd_value_union *subvalue; + + *err = NULL; + + /* 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; + subvalue->hints = hints; + subvalue->ctx_node = ctx_node; + + if (format == LY_VALUE_LYB) { + 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, NULL, unres, err); + LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup); + } + + /* store canonical value, if any (use the specific type value) */ + r = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical); + LY_CHECK_ERR_GOTO(r, ret = r, cleanup); + +cleanup: + if (options & LYPLG_TYPE_STORE_DYNAMIC) { + free((void *)value); + } + + if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) { + lyplg_type_free_union(ctx, storage); + } + return ret; +} + +LIBYANG_API_DEF 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) +{ + LY_ERR ret = LY_SUCCESS; + struct lysc_type_union *type_u = (struct lysc_type_union *)type; + struct lyd_value_union *subvalue = storage->subvalue; + + *err = NULL; + + /* because of types that do not store their own type as realtype (leafref), we are not able to call their + * validate callback (there is no way to get the type TODO could be added to struct lyd_value_union), so + * we have to perform union value storing again from scratch */ + subvalue->value.realtype->plugin->free(ctx, &subvalue->value); + + /* use the first usable subtype to store the value */ + 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 */ + lydict_remove(ctx, storage->_canonical); + LY_CHECK_RET(lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical)); + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2) +{ + if (val1->realtype != val2->realtype) { + return LY_ENOT; + } + + if (val1->subvalue->value.realtype != val2->subvalue->value.realtype) { + return LY_ENOT; + } + 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; + uint64_t num = 0; + uint32_t type_idx; + ly_bool dynamic; + 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_RET((retval != LY_SUCCESS) && (retval != LY_EINCOMPLETE), NULL); + + /* Print subvalue in LYB format. */ + pval = (void *)subvalue->value.realtype->plugin->print(NULL, &subvalue->value, LY_VALUE_LYB, prefix_data, &dynamic, + &pval_len); + LY_CHECK_RET(!pval, NULL); + + /* Create LYB data. */ + *value_len = IDX_SIZE + pval_len; + ret = malloc(*value_len); + LY_CHECK_RET(!ret, NULL); + + num = type_idx; + num = htole64(num); + memcpy(ret, &num, IDX_SIZE); + memcpy((char *)ret + IDX_SIZE, pval, pval_len); + + if (dynamic) { + free(pval); + } + + return ret; +} + +LIBYANG_API_DEF 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 = 0; + + 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)) { + /* the canonical value is supposed to be stored now */ + lydict_insert(ctx, subvalue->value._canonical, 0, (const char **)&value->_canonical); + } + + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_value_union *orig_val = original->subvalue, *dup_val; + + /* init dup value */ + memset(dup, 0, sizeof *dup); + dup->realtype = original->realtype; + + ret = lydict_insert(ctx, original->_canonical, 0, &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); + + if (orig_val->orig_len) { + dup_val->original = calloc(1, 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); + } else { + dup_val->original = strdup(""); + LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup); + } + dup_val->orig_len = orig_val->orig_len; + + dup_val->format = orig_val->format; + dup_val->ctx_node = orig_val->ctx_node; + dup_val->hints = orig_val->hints; + 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; +} + +LIBYANG_API_DEF void +lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value) +{ + struct lyd_value_union *val; + + lydict_remove(ctx, value->_canonical); + value->_canonical = NULL; + LYD_VALUE_GET(value, val); + if (val) { + if (val->value.realtype) { + val->value.realtype->plugin->free(ctx, &val->value); + } + lyplg_type_prefix_data_free(val->format, val->prefix_data); + free(val->original); + + LYPLG_TYPE_VAL_INLINE_DESTROY(val); + } +} + +/** + * @brief Plugin information for union type implementation. + * + * Note that external plugins are supposed to use: + * + * LYPLG_TYPES = { + */ +const struct lyplg_type_record plugins_union[] = { + { + .module = "", + .revision = NULL, + .name = LY_TYPE_UNION_STR, + + .plugin.id = "libyang 2 - union,version 1", + .plugin.store = lyplg_type_store_union, + .plugin.validate = lyplg_type_validate_union, + .plugin.compare = lyplg_type_compare_union, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_union, + .plugin.duplicate = lyplg_type_dup_union, + .plugin.free = lyplg_type_free_union, + .plugin.lyb_data_len = -1, + }, + {0} +}; |