From cd07912073c951b4bbb871ed2653af1be2cfc714 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 11:55:11 +0200 Subject: Adding upstream version 2.1.30. Signed-off-by: Daniel Baumann --- src/plugins_types/bits.c | 510 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 510 insertions(+) create mode 100644 src/plugins_types/bits.c (limited to 'src/plugins_types/bits.c') diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c new file mode 100644 index 0000000..04adace --- /dev/null +++ b/src/plugins_types/bits.c @@ -0,0 +1,510 @@ +/** + * @file bits.c + * @author Radek Krejci + * @brief Built-in bits 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 +#include +#include +#include + +#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 howtoDataLYBTypesBits bits (built-in) + * + * | Size (B) | Mandatory | Type | Meaning | + * | :------ | :-------: | :--: | :-----: | + * | returned by ::lyplg_type_bits_bitmap_size() | yes | pointer to integer type of the specific size, if size more than 8 use `char *` | bitmap of the set bits | + */ + +/** + * @brief Get the position of the last bit. + */ +#define BITS_LAST_BIT_POSITION(type_bits) (type_bits->bits[LY_ARRAY_COUNT(type_bits->bits) - 1].position) + +/** + * @brief Get a specific byte in a bitmap. + */ +#ifdef IS_BIG_ENDIAN +# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + (size - 1) - idx) +#else +# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + idx) +#endif + +LIBYANG_API_DEF size_t +lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type) +{ + size_t needed_bytes, size; + + LY_CHECK_ARG_RET(NULL, type, type->basetype == LY_TYPE_BITS, 0); + + /* minimum needed bytes to hold all the bit positions (which start at 0) */ + needed_bytes = ((BITS_LAST_BIT_POSITION(type) + 1) / 8) + ((BITS_LAST_BIT_POSITION(type) + 1) % 8 ? 1 : 0); + LY_CHECK_ERR_RET(!needed_bytes, LOGINT(NULL), 0); + + if ((needed_bytes == 1) || (needed_bytes == 2)) { + /* uint8_t or uint16_t */ + size = needed_bytes; + } else if (needed_bytes < 5) { + /* uint32_t */ + size = 4; + } else if (needed_bytes < 9) { + /* uint64_t */ + size = 8; + } else { + /* no basic type, do not round */ + size = needed_bytes; + } + + return size; +} + +LIBYANG_API_DEF ly_bool +lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position) +{ + char bitmask; + + /* find the byte with our bit */ + (void)size; + bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8); + bit_position %= 8; + + /* generate bitmask */ + bitmask = 1; + bitmask <<= bit_position; + + /* check if bit set */ + if (*bitmap & bitmask) { + return 1; + } + return 0; +} + +/** + * @brief Set bit at a specific position. + * + * @param[in,out] bitmap Bitmap to modify. + * @param[in] size Size of @p bitmap. + * @param[in] bit_position Bit position to set. + */ +static void +bits_bit_set(char *bitmap, size_t size, uint32_t bit_position) +{ + char bitmask; + + /* find the byte with our bit */ + (void)size; + bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8); + bit_position %= 8; + + /* generate bitmask */ + bitmask = 1; + bitmask <<= bit_position; + + /* set the bit */ + *bitmap |= bitmask; +} + +/** + * @brief Convert a list of bit names separated by whitespaces to a bitmap. + * + * @param[in] value Value to convert. + * @param[in] value_len Length of @p value. + * @param[in] type Type of the value. + * @param[in,out] bitmap Zeroed bitmap, is filled (set). + * @param[out] err Error information. + * @return LY_ERR value. + */ +static LY_ERR +bits_str2bitmap(const char *value, size_t value_len, struct lysc_type_bits *type, char *bitmap, struct ly_err_item **err) +{ + size_t idx_start, idx_end; + LY_ARRAY_COUNT_TYPE u; + ly_bool found; + + idx_start = idx_end = 0; + while (idx_end < value_len) { + /* skip whitespaces */ + while ((idx_end < value_len) && isspace(value[idx_end])) { + ++idx_end; + } + if (idx_end == value_len) { + break; + } + + /* parse bit name */ + idx_start = idx_end; + while ((idx_end < value_len) && !isspace(value[idx_end])) { + ++idx_end; + } + + /* find the bit */ + found = 0; + LY_ARRAY_FOR(type->bits, u) { + if (!ly_strncmp(type->bits[u].name, value + idx_start, idx_end - idx_start)) { + found = 1; + break; + } + } + + /* check if name exists */ + if (!found) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid bit \"%.*s\".", (int)(idx_end - idx_start), + value + idx_start); + } + + /* check for duplication */ + if (lyplg_type_bits_is_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position)) { + return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Duplicate bit \"%s\".", type->bits[u].name); + } + + /* set the bit */ + bits_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position); + } + + return LY_SUCCESS; +} + +/** + * @brief Add a bit item into an array. + * + * @param[in] position Bit position to add. + * @param[in] type Bitis type to read the bit positions and names from. + * @param[in,out] items Array of bit item pointers to add to. + */ +static void +bits_add_item(uint32_t position, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items) +{ + LY_ARRAY_COUNT_TYPE u; + + /* find the bit item */ + LY_ARRAY_FOR(type->bits, u) { + if (type->bits[u].position == position) { + break; + } + } + + /* add it at the end */ + items[LY_ARRAY_COUNT(items)] = &type->bits[u]; + LY_ARRAY_INCREMENT(items); +} + +/** + * @brief Convert a bitmap to a sized array of pointers to their bit definitions. + * + * @param[in] bitmap Bitmap to read from. + * @param[in] type Bits type. + * @param[in,out] items Allocated sized array to fill with the set bits. + */ +static void +bits_bitmap2items(const char *bitmap, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items) +{ + size_t i, bitmap_size = lyplg_type_bits_bitmap_size(type); + uint32_t bit_pos; + uint8_t bitmask; + const uint8_t *byte; + + bit_pos = 0; + for (i = 0; i < bitmap_size; ++i) { + /* check this byte (but not necessarily all bits in the last byte) */ + byte = (uint8_t *)BITS_BITMAP_BYTE(bitmap, bitmap_size, i); + for (bitmask = 1; bitmask; bitmask <<= 1) { + if (*byte & bitmask) { + /* add this bit */ + bits_add_item(bit_pos, type, items); + } + + if (bit_pos == BITS_LAST_BIT_POSITION(type)) { + /* we have checked the last valid bit */ + break; + } + + ++bit_pos; + } + } +} + +/** + * @brief Generate canonical value from ordered array of set bit items. + * + * @param[in] items Sized array of set bit items. + * @param[out] canonical Canonical string value. + * @return LY_ERR value. + */ +static LY_ERR +bits_items2canon(struct lysc_type_bitenum_item **items, char **canonical) +{ + char *ret; + size_t ret_len; + LY_ARRAY_COUNT_TYPE u; + + *canonical = NULL; + + /* init value */ + ret = strdup(""); + LY_CHECK_RET(!ret, LY_EMEM); + ret_len = 0; + + LY_ARRAY_FOR(items, u) { + if (!ret_len) { + ret = ly_realloc(ret, strlen(items[u]->name) + 1); + LY_CHECK_RET(!ret, LY_EMEM); + strcpy(ret, items[u]->name); + + ret_len = strlen(ret); + } else { + ret = ly_realloc(ret, ret_len + 1 + strlen(items[u]->name) + 1); + LY_CHECK_RET(!ret, LY_EMEM); + sprintf(ret + ret_len, " %s", items[u]->name); + + ret_len += 1 + strlen(items[u]->name); + } + } + + *canonical = ret; + return LY_SUCCESS; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_store_bits(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 *UNUSED(prefix_data), uint32_t hints, + const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres), + struct ly_err_item **err) +{ + LY_ERR ret = LY_SUCCESS; + struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type; + struct lyd_value_bits *val; + + /* 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 */ + if (value_len != lyplg_type_bits_bitmap_size(type_bits)) { + ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB bits value size %zu (expected %zu).", + value_len, lyplg_type_bits_bitmap_size(type_bits)); + goto cleanup; + } + + /* store value (bitmap) */ + if (options & LYPLG_TYPE_STORE_DYNAMIC) { + val->bitmap = (char *)value; + options &= ~LYPLG_TYPE_STORE_DYNAMIC; + } else { + val->bitmap = malloc(value_len); + LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup); + memcpy(val->bitmap, value, value_len); + } + + /* allocate and fill the bit item array */ + LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup); + bits_bitmap2items(val->bitmap, type_bits, val->items); + + /* success */ + goto cleanup; + } + + /* check hints */ + ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err); + LY_CHECK_GOTO(ret, cleanup); + + /* allocate the bitmap */ + val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits)); + LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup); + memset(val->bitmap, 0, lyplg_type_bits_bitmap_size(type_bits)); + + /* fill the bitmap */ + ret = bits_str2bitmap(value, value_len, type_bits, val->bitmap, err); + LY_CHECK_GOTO(ret, cleanup); + + /* allocate and fill the bit item array */ + LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup); + bits_bitmap2items(val->bitmap, type_bits, val->items); + + if (format == LY_VALUE_CANON) { + /* store canonical value */ + if (options & LYPLG_TYPE_STORE_DYNAMIC) { + ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical); + options &= ~LYPLG_TYPE_STORE_DYNAMIC; + LY_CHECK_GOTO(ret, cleanup); + } else { + ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical); + LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup); + } + } + +cleanup: + if (options & LYPLG_TYPE_STORE_DYNAMIC) { + free((void *)value); + } + + if (ret) { + lyplg_type_free_bits(ctx, storage); + } + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2) +{ + 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; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF const void * +lyplg_type_print_bits(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 lysc_type_bits *type_bits = (struct lysc_type_bits *)value->realtype; + struct lyd_value_bits *val; + char *ret; + + LYD_VALUE_GET(value, val); + + if (format == LY_VALUE_LYB) { + *dynamic = 0; + if (value_len) { + *value_len = lyplg_type_bits_bitmap_size(type_bits); + } + return val->bitmap; + } + + /* generate canonical value if not already */ + if (!value->_canonical) { + /* get the canonical value */ + if (bits_items2canon(val->items, &ret)) { + return NULL; + } + + /* store it */ + if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) { + LOGMEM(ctx); + return NULL; + } + } + + /* use the cached canonical value */ + if (dynamic) { + *dynamic = 0; + } + if (value_len) { + *value_len = strlen(value->_canonical); + } + return value->_canonical; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup) +{ + 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, *dup_val; + + memset(dup, 0, sizeof *dup); + + /* optional canonical value */ + ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical); + LY_CHECK_GOTO(ret, error); + + /* allocate value */ + LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val); + LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error); + + LYD_VALUE_GET(original, orig_val); + + /* duplicate bitmap */ + dup_val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits)); + LY_CHECK_ERR_GOTO(!dup_val->bitmap, ret = LY_EMEM, error); + memcpy(dup_val->bitmap, orig_val->bitmap, lyplg_type_bits_bitmap_size(type_bits)); + + /* duplicate bit item pointers */ + LY_ARRAY_CREATE_GOTO(ctx, dup_val->items, LY_ARRAY_COUNT(orig_val->items), ret, error); + LY_ARRAY_FOR(orig_val->items, u) { + LY_ARRAY_INCREMENT(dup_val->items); + dup_val->items[u] = orig_val->items[u]; + } + + dup->realtype = original->realtype; + return LY_SUCCESS; + +error: + lyplg_type_free_bits(ctx, dup); + return ret; +} + +LIBYANG_API_DEF void +lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value) +{ + struct lyd_value_bits *val; + + lydict_remove(ctx, value->_canonical); + value->_canonical = NULL; + LYD_VALUE_GET(value, val); + if (val) { + free(val->bitmap); + LY_ARRAY_FREE(val->items); + LYPLG_TYPE_VAL_INLINE_DESTROY(val); + } +} + +/** + * @brief Plugin information for bits type implementation. + * + * Note that external plugins are supposed to use: + * + * LYPLG_TYPES = { + */ +const struct lyplg_type_record plugins_bits[] = { + { + .module = "", + .revision = NULL, + .name = LY_TYPE_BITS_STR, + + .plugin.id = "libyang 2 - bits, version 1", + .plugin.store = lyplg_type_store_bits, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_bits, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_bits, + .plugin.duplicate = lyplg_type_dup_bits, + .plugin.free = lyplg_type_free_bits, + .plugin.lyb_data_len = -1, + }, + {0} +}; -- cgit v1.2.3