diff options
Diffstat (limited to '')
-rw-r--r-- | src/plugins_types/integer.c | 585 |
1 files changed, 585 insertions, 0 deletions
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c new file mode 100644 index 0000000..0903365 --- /dev/null +++ b/src/plugins_types/integer.c @@ -0,0 +1,585 @@ +/** + * @file integer.c + * @author Radek Krejci <rkrejci@cesnet.cz> + * @brief Built-in integer types 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 /* asprintf, strdup */ + +#include "plugins_types.h" + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.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 howtoDataLYBTypesInteger (u)int(8/16/32/64) (built-in) + * + * | Size (B) | Mandatory | Type | Meaning | + * | :------ | :-------: | :--: | :-----: | + * | 1/2/4/8 | yes | pointer to the specific integer type | little-endian integer value | + */ + +/** + * @brief LYB value size of each integer type. + */ +static size_t integer_lyb_size[] = { + [LY_TYPE_INT8] = 1, [LY_TYPE_INT16] = 2, [LY_TYPE_INT32] = 4, [LY_TYPE_INT64] = 8, + [LY_TYPE_UINT8] = 1, [LY_TYPE_UINT16] = 2, [LY_TYPE_UINT32] = 4, [LY_TYPE_UINT64] = 8 +}; + +LIBYANG_API_DEF LY_ERR +lyplg_type_store_int(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; + int64_t num = 0; + int base = 1; + char *canon = NULL; + struct lysc_type_num *type_num = (struct lysc_type_num *)type; + + /* init storage */ + memset(storage, 0, sizeof *storage); + storage->realtype = type; + + if (format == LY_VALUE_LYB) { + /* validation */ + if (value_len != integer_lyb_size[type->basetype]) { + ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB signed integer value size %zu (expected %zu).", + value_len, integer_lyb_size[type->basetype]); + goto cleanup; + } + + /* copy the integer and correct the byte order */ + memcpy(&num, value, value_len); + num = le64toh(num); + } else { + /* check hints */ + ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err); + LY_CHECK_GOTO(ret, cleanup); + + /* parse the integer */ + switch (type->basetype) { + case LY_TYPE_INT8: + ret = lyplg_type_parse_int("int8", base, INT64_C(-128), INT64_C(127), value, value_len, &num, err); + break; + case LY_TYPE_INT16: + ret = lyplg_type_parse_int("int16", base, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err); + break; + case LY_TYPE_INT32: + ret = lyplg_type_parse_int("int32", base, INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num, err); + break; + case LY_TYPE_INT64: + ret = lyplg_type_parse_int("int64", base, INT64_C(-9223372036854775807) - INT64_C(1), + INT64_C(9223372036854775807), value, value_len, &num, err); + break; + default: + LOGINT(ctx); + ret = LY_EINT; + } + LY_CHECK_GOTO(ret, cleanup); + } + + /* set the value (matters for big-endian) and get the correct int64 number */ + switch (type->basetype) { + case LY_TYPE_INT8: + storage->int8 = num; + num = storage->int8; + break; + case LY_TYPE_INT16: + storage->int16 = num; + num = storage->int16; + break; + case LY_TYPE_INT32: + storage->int32 = num; + num = storage->int32; + break; + case LY_TYPE_INT64: + storage->int64 = num; + num = storage->int64; + break; + default: + break; + } + + 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, value_len, &storage->_canonical); + LY_CHECK_GOTO(ret, cleanup); + } + } else { + /* generate canonical value */ + switch (type->basetype) { + case LY_TYPE_INT8: + LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId8, storage->int8) == -1, ret = LY_EMEM, cleanup); + break; + case LY_TYPE_INT16: + LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId16, storage->int16) == -1, ret = LY_EMEM, cleanup); + break; + case LY_TYPE_INT32: + LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId32, storage->int32) == -1, ret = LY_EMEM, cleanup); + break; + case LY_TYPE_INT64: + LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId64, storage->int64) == -1, ret = LY_EMEM, cleanup); + break; + default: + break; + } + + /* store it */ + ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical); + LY_CHECK_GOTO(ret, cleanup); + } + + /* validate range of the number */ + if (type_num->range) { + ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical, + strlen(storage->_canonical), err); + LY_CHECK_GOTO(ret, cleanup); + } + +cleanup: + if (options & LYPLG_TYPE_STORE_DYNAMIC) { + free((void *)value); + } + + if (ret) { + lyplg_type_free_simple(ctx, storage); + } + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_compare_int(const struct lyd_value *val1, const struct lyd_value *val2) +{ + if (val1->realtype != val2->realtype) { + return LY_ENOT; + } + + switch (val1->realtype->basetype) { + case LY_TYPE_INT8: + if (val1->int8 != val2->int8) { + return LY_ENOT; + } + break; + case LY_TYPE_INT16: + if (val1->int16 != val2->int16) { + return LY_ENOT; + } + break; + case LY_TYPE_INT32: + if (val1->int32 != val2->int32) { + return LY_ENOT; + } + break; + case LY_TYPE_INT64: + if (val1->int64 != val2->int64) { + return LY_ENOT; + } + break; + default: + break; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF const void * +lyplg_type_print_int(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format, + void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len) +{ + int64_t prev_num = 0, num = 0; + void *buf; + + if (format == LY_VALUE_LYB) { + switch (value->realtype->basetype) { + case LY_TYPE_INT8: + prev_num = num = value->int8; + break; + case LY_TYPE_INT16: + prev_num = num = value->int16; + break; + case LY_TYPE_INT32: + prev_num = num = value->int32; + break; + case LY_TYPE_INT64: + prev_num = num = value->int64; + break; + default: + break; + } + num = htole64(num); + if (num == prev_num) { + /* values are equal, little-endian or int8 */ + *dynamic = 0; + if (value_len) { + *value_len = integer_lyb_size[value->realtype->basetype]; + } + return &value->int64; + } else { + /* values differ, big-endian */ + buf = calloc(1, integer_lyb_size[value->realtype->basetype]); + LY_CHECK_RET(!buf, NULL); + + *dynamic = 1; + if (value_len) { + *value_len = integer_lyb_size[value->realtype->basetype]; + } + memcpy(buf, &num, integer_lyb_size[value->realtype->basetype]); + return buf; + } + } + + /* 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_store_uint(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; + uint64_t num = 0; + int base = 0; + char *canon; + struct lysc_type_num *type_num = (struct lysc_type_num *)type; + + /* init storage */ + memset(storage, 0, sizeof *storage); + storage->realtype = type; + + if (format == LY_VALUE_LYB) { + /* validation */ + if (value_len != integer_lyb_size[type->basetype]) { + ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB unsigned integer value size %zu (expected %zu).", + value_len, integer_lyb_size[type->basetype]); + goto cleanup; + } + + /* copy the integer and correct the byte order */ + memcpy(&num, value, value_len); + num = le64toh(num); + } else { + /* check hints */ + ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err); + LY_CHECK_GOTO(ret, cleanup); + + /* parse the integer */ + switch (type->basetype) { + case LY_TYPE_UINT8: + ret = lyplg_type_parse_uint("uint8", base, UINT64_C(255), value, value_len, &num, err); + break; + case LY_TYPE_UINT16: + ret = lyplg_type_parse_uint("uint16", base, UINT64_C(65535), value, value_len, &num, err); + break; + case LY_TYPE_UINT32: + ret = lyplg_type_parse_uint("uint32", base, UINT64_C(4294967295), value, value_len, &num, err); + break; + case LY_TYPE_UINT64: + ret = lyplg_type_parse_uint("uint64", base, UINT64_C(18446744073709551615), value, value_len, &num, err); + break; + default: + LOGINT(ctx); + ret = LY_EINT; + } + LY_CHECK_GOTO(ret, cleanup); + } + + /* store value, matters for big-endian */ + switch (type->basetype) { + case LY_TYPE_UINT8: + storage->uint8 = num; + break; + case LY_TYPE_UINT16: + storage->uint16 = num; + break; + case LY_TYPE_UINT32: + storage->uint32 = num; + break; + case LY_TYPE_UINT64: + storage->uint64 = num; + break; + default: + break; + } + + 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, value_len, &storage->_canonical); + LY_CHECK_GOTO(ret, cleanup); + } + } else { + /* generate canonical value */ + LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRIu64, num) == -1, ret = LY_EMEM, cleanup); + + /* store it */ + ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical); + LY_CHECK_GOTO(ret, cleanup); + } + + /* validate range of the number */ + if (type_num->range) { + ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical, + strlen(storage->_canonical), err); + LY_CHECK_GOTO(ret, cleanup); + } + +cleanup: + if (options & LYPLG_TYPE_STORE_DYNAMIC) { + free((void *)value); + } + + if (ret) { + lyplg_type_free_simple(ctx, storage); + } + return ret; +} + +LIBYANG_API_DEF LY_ERR +lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2) +{ + if (val1->realtype != val2->realtype) { + return LY_ENOT; + } + + switch (val1->realtype->basetype) { + case LY_TYPE_UINT8: + if (val1->uint8 != val2->uint8) { + return LY_ENOT; + } + break; + case LY_TYPE_UINT16: + if (val1->uint16 != val2->uint16) { + return LY_ENOT; + } + break; + case LY_TYPE_UINT32: + if (val1->uint32 != val2->uint32) { + return LY_ENOT; + } + break; + case LY_TYPE_UINT64: + if (val1->uint64 != val2->uint64) { + return LY_ENOT; + } + break; + default: + break; + } + return LY_SUCCESS; +} + +LIBYANG_API_DEF const void * +lyplg_type_print_uint(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format, + void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len) +{ + uint64_t num = 0; + void *buf; + + if (format == LY_VALUE_LYB) { + switch (value->realtype->basetype) { + case LY_TYPE_UINT8: + num = value->uint8; + break; + case LY_TYPE_UINT16: + num = value->uint16; + break; + case LY_TYPE_UINT32: + num = value->uint32; + break; + case LY_TYPE_UINT64: + num = value->uint64; + break; + default: + break; + } + num = htole64(num); + if (num == value->uint64) { + /* values are equal, little-endian or uint8 */ + *dynamic = 0; + if (value_len) { + *value_len = integer_lyb_size[value->realtype->basetype]; + } + return &value->uint64; + } else { + /* values differ, big-endian */ + buf = calloc(1, integer_lyb_size[value->realtype->basetype]); + LY_CHECK_RET(!buf, NULL); + + *dynamic = 1; + if (value_len) { + *value_len = integer_lyb_size[value->realtype->basetype]; + } + memcpy(buf, &num, integer_lyb_size[value->realtype->basetype]); + return buf; + } + } + + /* use the cached canonical value */ + if (dynamic) { + *dynamic = 0; + } + if (value_len) { + *value_len = strlen(value->_canonical); + } + return value->_canonical; +} + +/** + * @brief Plugin information for integer types implementation. + * + * Note that external plugins are supposed to use: + * + * LYPLG_TYPES = { + */ +const struct lyplg_type_record plugins_integer[] = { + { + .module = "", + .revision = NULL, + .name = LY_TYPE_UINT8_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_uint, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_uint, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_uint, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 1, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_UINT16_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_uint, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_uint, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_uint, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 2, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_UINT32_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_uint, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_uint, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_uint, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 4, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_UINT64_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_uint, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_uint, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_uint, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 8, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_INT8_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_int, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_int, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_int, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 1, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_INT16_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_int, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_int, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_int, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 2, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_INT32_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_int, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_int, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_int, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 4, + }, { + .module = "", + .revision = NULL, + .name = LY_TYPE_INT64_STR, + + .plugin.id = "libyang 2 - integers, version 1", + .plugin.store = lyplg_type_store_int, + .plugin.validate = NULL, + .plugin.compare = lyplg_type_compare_int, + .plugin.sort = NULL, + .plugin.print = lyplg_type_print_int, + .plugin.duplicate = lyplg_type_dup_simple, + .plugin.free = lyplg_type_free_simple, + .plugin.lyb_data_len = 8, + }, + {0} +}; |