Merge branch 'ly2_type_plugins' into libyang2
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 5f83c49..ef4085a 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -102,10 +102,13 @@
src/plugins_types/leafref.c
src/plugins_types/string.c
src/plugins_types/union.c
+ src/plugins_types/ipv4_address.c
+ src/plugins_types/ipv4_address_no_zone.c
src/plugins_types/ipv6_address.c
- src/plugins_types/ip_prefix.c
+ src/plugins_types/ipv6_address_no_zone.c
+ src/plugins_types/ipv4_prefix.c
+ src/plugins_types/ipv6_prefix.c
src/plugins_types/date_and_time.c
- src/plugins_types/hex_string.c
src/plugins_types/xpath1.0.c)
set(libsrc
diff --git a/src/common.c b/src/common.c
index 9c0d15a..a434235 100644
--- a/src/common.c
+++ b/src/common.c
@@ -19,6 +19,7 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
+#include <inttypes.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -46,12 +47,8 @@
char *
ly_strnchr(const char *s, int c, size_t len)
{
- for ( ; *s != (char)c; ++s, --len) {
- if ((*s == '\0') || (!len)) {
- return NULL;
- }
- }
- return (char *)s;
+ for ( ; len && (*s != (char)c); ++s, --len) {}
+ return len ? (char *)s : NULL;
}
int
@@ -66,6 +63,44 @@
}
}
+#define LY_OVERFLOW_ADD(MAX, X, Y) ((X > MAX - Y) ? 1 : 0)
+
+#define LY_OVERFLOW_MUL(MAX, X, Y) ((X > MAX / Y) ? 1 : 0)
+
+LY_ERR
+ly_strntou8(const char *nptr, size_t len, uint8_t *ret)
+{
+ uint8_t num = 0, dig, dec_pow;
+
+ if (len > 3) {
+ /* overflow for sure */
+ return LY_EDENIED;
+ }
+
+ dec_pow = 1;
+ for ( ; len && isdigit(nptr[len - 1]); --len) {
+ dig = nptr[len - 1] - 48;
+
+ if (LY_OVERFLOW_MUL(UINT8_MAX, dig, dec_pow)) {
+ return LY_EDENIED;
+ }
+ dig *= dec_pow;
+
+ if (LY_OVERFLOW_ADD(UINT8_MAX, num, dig)) {
+ return LY_EDENIED;
+ }
+ num += dig;
+
+ dec_pow *= 10;
+ }
+
+ if (len) {
+ return LY_EVALID;
+ }
+ *ret = num;
+ return LY_SUCCESS;
+}
+
uint32_t
ly_value_prefix_next(const char *str_begin, const char *str_end, ly_bool *is_prefix, const char **str_next)
{
@@ -293,7 +328,7 @@
size_t len = 0;
const char *ptr = str;
- while (*ptr && (size_t)(ptr - str) < bytes) {
+ while (((size_t)(ptr - str) < bytes) && *ptr) {
++len;
ptr += utf8_char_length_table[((unsigned char)(*ptr))];
}
@@ -402,60 +437,81 @@
LY_ERR
ly_parse_int(const char *val_str, size_t val_len, int64_t min, int64_t max, int base, int64_t *ret)
{
- char *strptr;
+ LY_ERR rc = LY_SUCCESS;
+ char *ptr, *str;
int64_t i;
LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
- /* convert to 64-bit integer, all the redundant characters are handled */
- errno = 0;
- strptr = NULL;
+ /* duplicate the value */
+ str = strndup(val_str, val_len);
+ LY_CHECK_RET(!str, LY_EMEM);
- /* parse the value */
- i = strtoll(val_str, &strptr, base);
- if (errno || (strptr == val_str)) {
- return LY_EVALID;
+ /* parse the value to avoid accessing following bytes */
+ errno = 0;
+ i = strtoll(str, &ptr, base);
+ if (errno || (ptr == str)) {
+ /* invalid string */
+ rc = LY_EVALID;
} else if ((i < min) || (i > max)) {
- return LY_EDENIED;
- } else if (strptr && *strptr) {
- while (isspace(*strptr)) {
- ++strptr;
+ /* invalid number */
+ rc = LY_EDENIED;
+ } else if (*ptr) {
+ while (isspace(*ptr)) {
+ ++ptr;
}
- if (*strptr && (strptr < val_str + val_len)) {
- return LY_EVALID;
+ if (*ptr) {
+ /* invalid characters after some number */
+ rc = LY_EVALID;
}
}
- *ret = i;
- return LY_SUCCESS;
+ /* cleanup */
+ free(str);
+ if (!rc) {
+ *ret = i;
+ }
+ return rc;
}
LY_ERR
ly_parse_uint(const char *val_str, size_t val_len, uint64_t max, int base, uint64_t *ret)
{
- char *strptr;
+ LY_ERR rc = LY_SUCCESS;
+ char *ptr, *str;
uint64_t u;
- LY_CHECK_ARG_RET(NULL, val_str, val_str[0], LY_EINVAL);
+ LY_CHECK_ARG_RET(NULL, val_str, val_str[0], val_len, LY_EINVAL);
+ /* duplicate the value to avoid accessing following bytes */
+ str = strndup(val_str, val_len);
+ LY_CHECK_RET(!str, LY_EMEM);
+
+ /* parse the value */
errno = 0;
- strptr = NULL;
- u = strtoull(val_str, &strptr, base);
- if (errno || (strptr == val_str)) {
- return LY_EVALID;
- } else if ((u > max) || (u && (val_str[0] == '-'))) {
- return LY_EDENIED;
- } else if (strptr && *strptr) {
- while (isspace(*strptr)) {
- ++strptr;
+ u = strtoull(str, &ptr, base);
+ if (errno || (ptr == str)) {
+ /* invalid string */
+ rc = LY_EVALID;
+ } else if ((u > max) || (u && (str[0] == '-'))) {
+ /* invalid number */
+ rc = LY_EDENIED;
+ } else if (*ptr) {
+ while (isspace(*ptr)) {
+ ++ptr;
}
- if (*strptr && (strptr < val_str + val_len)) {
- return LY_EVALID;
+ if (*ptr) {
+ /* invalid characters after some number */
+ rc = LY_EVALID;
}
}
- *ret = u;
- return LY_SUCCESS;
+ /* cleanup */
+ free(str);
+ if (!rc) {
+ *ret = u;
+ }
+ return rc;
}
/**
diff --git a/src/common.h b/src/common.h
index 6475c22..7a08f97 100644
--- a/src/common.h
+++ b/src/common.h
@@ -285,8 +285,8 @@
#define LY_VCODE_NOKEY LYVE_DATA, "List instance is missing its key \"%s\"."
#define LY_ERRMSG_NOPATTERN /* LYVE_DATA */ "Unsatisfied pattern - \"%.*s\" does not conform to %s\"%s\"."
-#define LY_ERRMSG_NOLENGTH /* LYVE_DATA */ "Unsatisfied length - string length \"%s\" is not allowed."
-#define LY_ERRMSG_NORANGE /* LYVE_DATA */ "Unsatisfied range - value \"%s\" is out of the allowed range."
+#define LY_ERRMSG_NOLENGTH /* LYVE_DATA */ "Unsatisfied length - string \"%.*s\" length is not allowed."
+#define LY_ERRMSG_NORANGE /* LYVE_DATA */ "Unsatisfied range - value \"%.*s\" is out of the allowed range."
/* RFC 7950 section 15 errors */
#define LY_VCODE_NOUNIQ LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
@@ -385,6 +385,19 @@
int ly_strncmp(const char *refstr, const char *str, size_t str_len);
/**
+ * @brief Similar functionality to strtoul() except number length in the string
+ * must be specified and the whole number must be parsed for success.
+ *
+ * @param[in] nptr Number string.
+ * @param[in] len Number string length starting at @p nptr.
+ * @param[out] ret Parsed number.
+ * @return LY_EDENIED on overflow.
+ * @return LY_EVALID on encountering a non-digit character.
+ * @return LY_SUCCESS on success.
+ */
+LY_ERR ly_strntou8(const char *nptr, size_t len, uint8_t *ret);
+
+/**
* @brief Similar to strlen(3) but accepts NULL and returns 0.
*
* @param[in] s String to examine.
diff --git a/src/plugins.c b/src/plugins.c
index d0b28a0..85e3745 100644
--- a/src/plugins.c
+++ b/src/plugins.c
@@ -54,14 +54,17 @@
/*
* ietf-inet-types
*/
-extern const struct lyplg_type_record plugins_ip_address[];
-extern const struct lyplg_type_record plugins_ip_prefix[];
+extern const struct lyplg_type_record plugins_ipv4_address[];
+extern const struct lyplg_type_record plugins_ipv4_address_no_zone[];
+extern const struct lyplg_type_record plugins_ipv6_address[];
+extern const struct lyplg_type_record plugins_ipv6_address_no_zone[];
+extern const struct lyplg_type_record plugins_ipv4_prefix[];
+extern const struct lyplg_type_record plugins_ipv6_prefix[];
/*
* ietf-yang-types
*/
extern const struct lyplg_type_record plugins_date_and_time[];
-extern const struct lyplg_type_record plugins_hex_string[];
extern const struct lyplg_type_record plugins_xpath10[];
/*
@@ -420,12 +423,15 @@
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_union), error);
/* ietf-inet-types */
- LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ip_address), error);
- LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ip_prefix), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_address_no_zone), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_address_no_zone), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv4_prefix), error);
+ LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_ipv6_prefix), error);
/* ietf-yang-types */
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_date_and_time), error);
- LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_hex_string), error);
LY_CHECK_GOTO(ret = plugins_insert(LYPLG_TYPE, plugins_xpath10), error);
/* internal extensions */
diff --git a/src/plugins_types.c b/src/plugins_types.c
index f4790f9..9ea7fcb 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -459,8 +459,8 @@
memset(&valcopy[len], '0', fraction_digits);
}
- ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1), INT64_C(9223372036854775807),
- valcopy, len, &d, err);
+ ret_val = lyplg_type_parse_int("decimal64", LY_BASE_DEC, INT64_C(-9223372036854775807) - INT64_C(1),
+ INT64_C(9223372036854775807), valcopy, size - 1, &d, err);
if (!ret_val && ret) {
*ret = d;
}
@@ -518,7 +518,7 @@
API LY_ERR
lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
- struct ly_err_item **err)
+ size_t strval_len, struct ly_err_item **err)
{
LY_ARRAY_COUNT_TYPE u;
ly_bool is_length; /* length or range */
@@ -535,7 +535,7 @@
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
- is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, strval);
+ is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
} else if ((uint64_t)value <= range->parts[u].max_u64) {
/* inside the range */
@@ -547,7 +547,7 @@
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
} else {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
- is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, strval);
+ is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
}
} else {
@@ -557,7 +557,7 @@
if (range->emsg) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
} else {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, strval);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
} else if (value <= range->parts[u].max_64) {
/* inside the range */
@@ -568,7 +568,7 @@
if (range->emsg) {
return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, range->emsg);
} else {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, strval);
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
}
}
}
@@ -702,18 +702,17 @@
LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL);
+ *path = NULL;
*err = NULL;
switch (format) {
- case LY_VALUE_CANON:
- LOGARG(ctx, format);
- return LY_EINVAL;
case LY_VALUE_SCHEMA:
case LY_VALUE_SCHEMA_RESOLVED:
case LY_VALUE_XML:
- case LY_VALUE_LYB:
prefix_opt = LY_PATH_PREFIX_MANDATORY;
break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_LYB:
case LY_VALUE_JSON:
prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
break;
@@ -770,7 +769,7 @@
}
API LY_ERR
-lyplg_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *der)
+lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *der)
{
LY_ARRAY_COUNT_TYPE u;
diff --git a/src/plugins_types.h b/src/plugins_types.h
index d104b45..3e29f16 100644
--- a/src/plugins_types.h
+++ b/src/plugins_types.h
@@ -39,6 +39,7 @@
struct lysc_pattern;
struct lysc_range;
struct lysc_type;
+struct lysc_type_bits;
struct lysc_type_leafref;
/**
@@ -248,6 +249,27 @@
LY_ERR lyplg_type_make_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres);
/**
+ * @brief Get the bitmap size of a bits value bitmap.
+ *
+ * Bitmap size is rounded up to the smallest integer size (1, 2, 4, or 8 bytes).
+ * If more than 8 bytes are needed to hold all the bit positions, no rounding is performed.
+ *
+ * @param[in] type Bits type.
+ * @return Bitmap size in bytes.
+ */
+size_t lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type);
+
+/**
+ * @brief Check whether a particular bit of a bitmap is set.
+ *
+ * @param[in] bitmap Bitmap to read from.
+ * @param[in] size Size of @p bitmap.
+ * @param[in] bit_position Bit position to check.
+ * @return Whether the bit is set or not.
+ */
+ly_bool lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position);
+
+/**
* @brief Get format-specific prefix for a module.
*
* Use only in implementations of ::lyplg_type_print_clb which provide all the necessary parameters for this function.
@@ -342,10 +364,10 @@
*
* @{
*/
-#define LYPLG_TYPE_STORE_DYNAMIC 0x01 /**< String value was dynamically allocated and is supposed to be freed or
+#define LYPLG_TYPE_STORE_DYNAMIC 0x01 /**< Value was dynamically allocated in its exact size and is supposed to be freed or
directly inserted into the context's dictionary (e.g. in case of canonization).
- In any case, the caller of the callback does not free the provided string
- value after calling the type's store callback with this option */
+ In any case, the caller of the callback does not free the provided
+ value after calling the type's store callback with this option. */
#define LYPLG_TYPE_STORE_IMPLEMENT 0x02 /**< If a foreign module is needed to be implemented to successfully instantiate
the value, make the module implemented. */
/** @} plugintypestoreopts */
@@ -358,13 +380,13 @@
* and stored on-demand. But if @p format is ::LY_VALUE_CANON (or another, which must be equal to the canonical
* value), the canonical value should be stored so that it does not have to be generated later.
*
- * Note that the @p value is not necessarily zero-terminated. The provided @p value_len is always correct.
- * All store functions have to free a dynamically allocated @p value in all cases (even error).
+ * Note that the @p value is not necessarily used whole (may not be zero-terminated if a string). The provided
+ * @p value_len is always correct. All store functions have to free a dynamically allocated @p value in all
+ * cases (even on error).
*
* @param[in] ctx libyang Context
* @param[in] type Type of the value being stored.
- * @param[in] value Lexical representation of the value to be stored.
- * It is never NULL, empty string is represented as "" with zero @p value_len.
+ * @param[in] value Value to be stored.
* @param[in] value_len Length (number of bytes) of the given @p value.
* @param[in] options [Type plugin store options](@ref plugintypestoreopts).
* @param[in] format Input format of the value, see the description for details.
@@ -514,38 +536,33 @@
* @ingroup pluginsTypes
* @{
*
- * Simple functions implementing @ref howtoPluginsTypes callbacks handling simply just the canonical string of the value
- * (::lyd_value._canonical).
+ * Simple functions implementing @ref howtoPluginsTypes callbacks handling types that allocate no dynamic
+ * value and always generate their canonical value (::lyd_value._canonical).
*/
/**
- * @brief Generic simple comparison callback checking the canonical value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for a generic simple type.
*/
LY_ERR lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2);
/**
- * @brief Generic simple printer callback of the canonized value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for a generic simple type.
*/
const void *lyplg_type_print_simple(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 Generic simple hash callback of the canonized value.
- * Implementation of the ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for a generic simple type.
*/
const void *lyplg_type_hash_simple(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
/**
- * @brief Generic simple duplication callback.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for a generic simple type.
*/
LY_ERR lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
/**
- * @brief Generic cleanup callback freeing only the canonized value in ::lyd_value._canonical.
- * Simple implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for a generic simple type.
*/
void lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value);
@@ -556,17 +573,42 @@
* @ingroup pluginsTypes
* @{
*
- * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement binary built-in type.
+ * Callbacks used to implement binary built-in type.
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in binary type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in binary type.
*/
LY_ERR lyplg_type_store_binary(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 Implementation of ::lyplg_type_compare_clb for the built-in binary type.
+ */
+LY_ERR lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in binary type.
+ */
+const void *lyplg_type_print_binary(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 Implementation of ::lyplg_type_hash_clb for the built-in binary type.
+ */
+const void *lyplg_type_hash_binary(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in binary type.
+ */
+LY_ERR lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in binary type.
+ */
+void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
+
/** @} pluginsTypesBinary */
/**
@@ -578,22 +620,35 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in bits type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of the ::lyplg_type_store_clb for the built-in bits type.
*/
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 *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 Duplication callback of the bits values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of the ::lyplg_type_compare_clb for the built-in bits type.
+ */
+LY_ERR lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of the ::lyplg_type_print_clb for the built-in bits type.
+ */
+const void *lyplg_type_print_bits(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 Implementation of the ::lyplg_type_hash_clb for the built-in bits type.
+ */
+const void *lyplg_type_hash_bits(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
+
+/**
+ * @brief Implementation of the ::lyplg_type_dup_clb for the built-in bits type.
*/
LY_ERR lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
/**
- * @brief Free value of the YANG built-in bits type.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of the ::lyplg_type_free_clb for the built-in bits type.
*/
void lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value);
@@ -608,8 +663,7 @@
*/
/**
- * @brief Validate and store value of the YANG built-in boolean type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in boolean type.
*/
LY_ERR lyplg_type_store_boolean(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,
@@ -626,8 +680,7 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in decimal64 types.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in decimal64 type.
*/
LY_ERR lyplg_type_store_decimal64(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,
@@ -644,8 +697,7 @@
*/
/**
- * @brief Validate and store value of the YANG built-in empty type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in empty type.
*/
LY_ERR lyplg_type_store_empty(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,
@@ -662,13 +714,18 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in enumeration type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in enumeration type.
*/
LY_ERR lyplg_type_store_enum(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 Implementation of ::lyplg_type_print_clb for the built-in enumeration type.
+ */
+const void *lyplg_type_print_enum(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
/** @} pluginsTypesEnumeration */
/**
@@ -680,22 +737,19 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in identiytref type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in identityref type.
*/
LY_ERR lyplg_type_store_identityref(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 for built-in identityref type.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in identityref type.
*/
LY_ERR lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2);
/**
- * @brief Printer callback printing identityref value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in identityref type.
*/
const void *lyplg_type_print_identityref(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
void *prefix_data, ly_bool *dynamic, size_t *value_len);
@@ -711,42 +765,36 @@
*/
/**
- * @brief Validate and store value of the YANG built-in instance-identifier type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in instance-identifier type.
*/
LY_ERR lyplg_type_store_instanceid(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 instance-identifier value.
- * Implementation of the ::lyplg_type_compare_clb.
- */
-LY_ERR lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2);
-
-/**
- * @brief Printer callback printing the instance-identifier value.
- * Implementation of the ::lyplg_type_print_clb.
- */
-const void *lyplg_type_print_instanceid(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 Duplication callback of the instance-identifier values.
- * Implementation of the ::lyplg_type_dup_clb.
- */
-LY_ERR lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
-
-/**
- * @brief Validate value of the YANG built-in instance-identifier type.
- * Implementation of the ::lyplg_type_validate_clb.
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in instance-identifier type.
*/
LY_ERR lyplg_type_validate_instanceid(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 instance-identifier types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in instance-identifier type.
+ */
+LY_ERR lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in instance-identifier type.
+ */
+const void *lyplg_type_print_instanceid(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 Implementation of ::lyplg_type_dup_clb for the built-in instance-identifier type.
+ */
+LY_ERR lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in instance-identifier type.
*/
void lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value);
@@ -761,16 +809,14 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in signed integer types.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in signed integer types.
*/
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 *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 Validate and canonize value of the YANG built-in unsigned integer types.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in unsigned integer types.
*/
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 *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -787,48 +833,41 @@
*/
/**
- * @brief Validate, canonize and store value of the YANG built-in leafref type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in leafref type.
*/
LY_ERR lyplg_type_store_leafref(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 leafref value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the built-in leafref type.
*/
LY_ERR lyplg_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2);
/**
- * @brief Printer callback printing the leafref value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in leafref type.
*/
const void *lyplg_type_print_leafref(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 leafref value.
- * Implementation of ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for the built-in leafref type.
*/
const void *lyplg_type_hash_leafref(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len);
/**
- * @brief Duplication callback of the leafref values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the built-in leafref type.
*/
LY_ERR lyplg_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
/**
- * @brief Validate value of the YANG built-in leafref type.
- * Implementation of the ::lyplg_type_validate_clb.
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in leafref type.
*/
LY_ERR lyplg_type_validate_leafref(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 leafref types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for the built-in leafref type.
*/
void lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value);
@@ -843,8 +882,7 @@
*/
/**
- * @brief Validate and store value of the YANG built-in string type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in string type.
*/
LY_ERR lyplg_type_store_string(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,
@@ -861,48 +899,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);
@@ -917,35 +948,30 @@
*/
/**
- * @brief Validate, canonize and store value of the ietf-yang-types xpath1.0 type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the ietf-yang-types xpath1.0 type.
*/
LY_ERR lyplg_type_store_xpath10(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 xpath1.0 value.
- * Implementation of the ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-yang-types xpath1.0 type.
*/
LY_ERR lyplg_type_compare_xpath10(const struct lyd_value *val1, const struct lyd_value *val2);
/**
- * @brief Printer callback printing the xpath1.0 value.
- * Implementation of the ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the ietf-yang-types xpath1.0 type.
*/
const void *lyplg_type_print_xpath10(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 Duplication callback of the xpath1.0 values.
- * Implementation of the ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-yang-types xpath1.0 type.
*/
LY_ERR lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
/**
- * @brief Free value of the derived xpath1.0 types.
- * Implementation of the ::lyplg_type_free_clb.
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-yang-types xpath1.0 type.
*/
void lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value);
@@ -1005,7 +1031,7 @@
* @return LY_SUCCESS if @p derived IS based on the @p base identity.
* @return LY_ENOTFOUND if @p derived IS NOT not based on the @p base identity.
*/
-LY_ERR lyplg_type_identity_isderived(struct lysc_ident *base, struct lysc_ident *derived);
+LY_ERR lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *derived);
/**
* @brief Data type validator for a range/length-restricted values.
@@ -1014,11 +1040,12 @@
* @param[in] range Range (length) restriction information.
* @param[in] value Value to check. In case of basetypes using unsigned integer values, the value is actually cast to uint64_t.
* @param[in] strval String representation of the @p value for error logging.
+ * @param[in] strval_len Length of @p strval.
* @param[out] err Error information in case of failure. The error structure can be freed by ::ly_err_free().
* @return LY_ERR value according to the result of the validation.
*/
LY_ERR lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
- struct ly_err_item **err);
+ size_t strval_len, struct ly_err_item **err);
/**
* @brief Data type validator for pattern-restricted string values.
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
index 23f410b..195548d 100644
--- a/src/plugins_types/binary.c
+++ b/src/plugins_types/binary.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <ctype.h>
@@ -28,104 +26,376 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBinary binary (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | binary value size | yes | `void *` | value in binary |
+ */
+
+/**
+ * @brief base64 encode table
+ */
+static const char b64_etable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * @brief Encode binary value into a base64 string value.
+ *
+ * Reference https://tools.ietf.org/html/rfc4648#section-4
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] data Binary data value.
+ * @param[in] size Size of @p data.
+ * @param[out] str Encoded base64 string.
+ * @param[out] str_len Length of returned @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_encode(const struct ly_ctx *ctx, const char *data, size_t size, char **str, size_t *str_len)
+{
+ uint32_t i;
+ char *ptr;
+
+ *str_len = (size + 2) / 3 * 4;
+ *str = malloc(*str_len + 1);
+ LY_CHECK_ERR_RET(!*str, LOGMEM(ctx), LY_EMEM);
+
+ ptr = *str;
+ for (i = 0; i < size - 2; i += 3) {
+ *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
+ *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
+ *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)];
+ *ptr++ = b64_etable[data[i + 2] & 0x3F];
+ }
+ if (i < size) {
+ *ptr++ = b64_etable[(data[i] >> 2) & 0x3F];
+ if (i == (size - 1)) {
+ *ptr++ = b64_etable[((data[i] & 0x3) << 4)];
+ *ptr++ = '=';
+ } else {
+ *ptr++ = b64_etable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)];
+ *ptr++ = b64_etable[((data[i + 1] & 0xF) << 2)];
+ }
+ *ptr++ = '=';
+ }
+ *ptr = '\0';
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief base64 decode table
+ */
+static const int b64_dtable[256] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
+ 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0,
+ 0, 0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+};
+
+/**
+ * @brief Decode the binary value from a base64 string value.
+ *
+ * Reference https://tools.ietf.org/html/rfc4648#section-4
+ *
+ * @param[in] value Base64-encoded string value.
+ * @param[in] value_len Length of @p value.
+ * @param[out] data Decoded binary value.
+ * @param[out] size Size of @p data.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_decode(const char *value, size_t value_len, void **data, size_t *size)
+{
+ unsigned char *ptr = (unsigned char *)value;
+ uint32_t pad_chars, octet_count;
+ char *str;
+
+ if (!value_len || (ptr[value_len - 1] != '=')) {
+ pad_chars = 0;
+ } else if (ptr[value_len - 2] == '=') {
+ pad_chars = 1;
+ } else {
+ pad_chars = 2;
+ }
+
+ octet_count = ((value_len + 3) / 4 - (pad_chars ? 1 : 0)) * 4;
+ *size = octet_count / 4 * 3 + pad_chars;
+
+ str = malloc(*size + 1);
+ LY_CHECK_RET(!str, LY_EMEM);
+ str[*size] = '\0';
+
+ for (uint32_t i = 0, j = 0; i < octet_count; i += 4) {
+ int n = b64_dtable[ptr[i]] << 18 | b64_dtable[ptr[i + 1]] << 12 | b64_dtable[ptr[i + 2]] << 6 | b64_dtable[ptr[i + 3]];
+ str[j++] = n >> 16;
+ str[j++] = n >> 8 & 0xFF;
+ str[j++] = n & 0xFF;
+ }
+ if (pad_chars) {
+ int n = b64_dtable[ptr[octet_count]] << 18 | b64_dtable[ptr[octet_count + 1]] << 12;
+ str[*size - pad_chars] = n >> 16;
+
+ if (pad_chars == 2) {
+ n |= b64_dtable[ptr[octet_count + 2]] << 6;
+ n >>= 8 & 0xFF;
+ str[*size - pad_chars + 1] = n;
+ }
+ }
+
+ *data = str;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Validate a base64 string.
+ *
+ * @param[in] value Value to validate.
+ * @param[in] value_len Length of @p value.
+ * @param[in] type type of the value.
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_validate(const char *value, size_t value_len, const struct lysc_type_bin *type, struct ly_err_item **err)
+{
+ uint32_t idx, pad;
+
+ /* check correct characters in base64 */
+ idx = 0;
+ while ((idx < value_len) &&
+ ((('A' <= value[idx]) && (value[idx] <= 'Z')) ||
+ (('a' <= value[idx]) && (value[idx] <= 'z')) ||
+ (('0' <= value[idx]) && (value[idx] <= '9')) ||
+ ('+' == value[idx]) || ('/' == value[idx]))) {
+ idx++;
+ }
+
+ /* find end of padding */
+ pad = 0;
+ while ((idx + pad < value_len) && (pad < 2) && (value[idx + pad] == '=')) {
+ pad++;
+ }
+
+ /* check if value is valid base64 value */
+ if (value_len != idx + pad) {
+ if (isprint(value[idx + pad])) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character '%c'.", value[idx + pad]);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character 0x%x.", value[idx + pad]);
+ }
+ }
+
+ if (value_len & 3) {
+ /* base64 length must be multiple of 4 chars */
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
+ }
+
+ /* length restriction of the binary value */
+ if (type->length) {
+ const uint32_t octet_count = ((idx + pad) / 4) * 3 - pad;
+ LY_CHECK_RET(lyplg_type_validate_range(LY_TYPE_BINARY, type->length, octet_count, value, value_len, err));
+ }
+
+ return LY_SUCCESS;
+}
+
API LY_ERR
lyplg_type_store_binary(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
- uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ 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;
- size_t base64_start, base64_end, base64_count, base64_terminated, value_end;
- const char *value_str = value;
struct lysc_type_bin *type_bin = (struct lysc_type_bin *)type;
+ struct lyd_value_binary *val;
- LY_CHECK_ARG_RET(ctx, value, LY_EINVAL);
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- *err = NULL;
+ if (format == LY_VALUE_LYB) {
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->bin = val;
+ storage->realtype = type;
+
+ /* store value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val->data = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ val->data = malloc(value_len);
+ LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
+ memcpy(val->data, value, value_len);
+ }
+
+ /* store size */
+ val->size = value_len;
+
+ /* success */
+ goto cleanup;
+ }
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
- /* validate characters and remember the number of octets for length validation */
- /* silently skip leading whitespaces */
- base64_start = 0;
- while (base64_start < value_len && isspace(value_str[base64_start])) {
- base64_start++;
- }
- /* silently skip trailing whitespace */
- value_end = value_len;
- while (base64_start < value_end && isspace(value_str[value_end - 1])) {
- value_end--;
+ /* validate */
+ if (format != LY_VALUE_CANON) {
+ ret = binary_base64_validate(value, value_len, type_bin, err);
+ LY_CHECK_GOTO(ret, cleanup);
}
- /* find end of base64 value */
- base64_end = base64_start;
- base64_count = 0;
- while ((base64_end < value_len) &&
- /* check correct character in base64 */
- ((('A' <= value_str[base64_end]) && (value_str[base64_end] <= 'Z')) ||
- (('a' <= value_str[base64_end]) && (value_str[base64_end] <= 'z')) ||
- (('0' <= value_str[base64_end]) && (value_str[base64_end] <= '9')) ||
- ('+' == value_str[base64_end]) ||
- ('/' == value_str[base64_end]) ||
- ('\n' == value_str[base64_end]))) {
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
- if ('\n' != value_str[base64_end]) {
- base64_count++;
- }
- base64_end++;
- }
-
- /* find end of padding */
- base64_terminated = 0;
- while (((base64_end < value_len) && (base64_terminated < 2)) &&
- /* check padding on end of string */
- (('=' == value_str[base64_end]) ||
- ('\n' == value_str[base64_end]))) {
-
- if ('\n' != value_str[base64_end]) {
- base64_terminated++;
- }
- base64_end++;
- }
-
- /* check if value is valid base64 value */
- if (value_end != base64_end) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid Base64 character (%c).", value_str[base64_end]);
- goto cleanup;
- }
-
- if ((base64_count + base64_terminated) & 3) {
- /* base64 length must be multiple of 4 chars */
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Base64 encoded value length must be divisible by 4.");
- goto cleanup;
- }
-
- /* check if value meets the type requirements */
- if (type_bin->length) {
- const uint32_t value_length = ((base64_count + base64_terminated) / 4) * 3 - base64_terminated;
- ret = lyplg_type_validate_range(LY_TYPE_BINARY, type_bin->length, value_length, value_str, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- }
-
- if (base64_count != 0) {
- ret = lydict_insert(ctx, &value_str[base64_start], base64_end - base64_start, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- } else {
- ret = lydict_insert(ctx, "", 0, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- }
- storage->ptr = NULL;
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->bin = val;
storage->realtype = type;
+ /* get the binary value */
+ ret = binary_base64_decode(value, value_len, &val->data, &val->size);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store canonical value, it always is */
+ 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, cleanup);
+ }
+
cleanup:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_binary(ctx, storage);
}
return ret;
}
+API LY_ERR
+lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_binary *v1 = val1->bin, *v2 = val2->bin;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+API const void *
+lyplg_type_print_binary(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 lyd_value_binary *val = value->bin;
+ char *ret;
+ size_t ret_len = 0;
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = val->size;
+ }
+ return val->data;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the base64 string value */
+ if (binary_base64_encode(ctx, val->data, val->size, &ret, &ret_len)) {
+ 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 = ret_len ? ret_len : strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+API const void *
+lyplg_type_hash_binary(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ struct lyd_value_binary *val = value->bin;
+
+ /* return the value itself */
+ *dynamic = 0;
+ *key_len = val->size;
+ return val->data;
+}
+
+API LY_ERR
+lyplg_type_dup_binary(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_binary *orig_val = original->bin, *dup_val;
+
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_RET(ret);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ if (!dup_val) {
+ lydict_remove(ctx, dup->_canonical);
+ return LY_EMEM;
+ }
+
+ dup_val->data = malloc(orig_val->size);
+ if (!dup_val->data) {
+ lydict_remove(ctx, dup->_canonical);
+ free(dup_val);
+ return LY_EMEM;
+ }
+ memcpy(dup_val->data, orig_val->data, orig_val->size);
+ dup_val->size = orig_val->size;
+
+ dup->bin = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+API void
+lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_binary *val = value->bin;
+
+ lydict_remove(ctx, value->_canonical);
+ if (val) {
+ free(val->data);
+ free(val);
+ }
+}
+
/**
* @brief Plugin information for binray type implementation.
*
@@ -142,11 +412,11 @@
.plugin.id = "libyang 2 - binary, version 1",
.plugin.store = lyplg_type_store_binary,
.plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple,
+ .plugin.compare = lyplg_type_compare_binary,
+ .plugin.print = lyplg_type_print_binary,
+ .plugin.hash = lyplg_type_hash_binary,
+ .plugin.duplicate = lyplg_type_dup_binary,
+ .plugin.free = lyplg_type_free_binary,
},
{0}
};
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
index a74ada4..7c48c85 100644
--- a/src/plugins_types/bits.c
+++ b/src/plugins_types/bits.c
@@ -12,7 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
#include "plugins_types.h"
@@ -29,197 +30,479 @@
#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
+
+API 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 */
+ needed_bytes = (BITS_LAST_BIT_POSITION(type) / 8) + (BITS_LAST_BIT_POSITION(type) % 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;
+}
+
+API 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;
+ char bitmask;
+ const char *byte;
+
+ bit_pos = 0;
+ for (i = 0; i < bitmap_size; ++i) {
+ /* check this byte (but not necessarily all bits in the last byte) */
+ byte = 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;
+}
+
API 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 UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ 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 lysc_type_bitenum_item **bits_items = NULL;
- struct ly_set *items = NULL;
- const char *value_str = value;
+ struct lyd_value_bits *val;
- uint32_t index_start; /* start index of bit name */
- uint32_t index_end = 0; /* end index of bit name */
- uint32_t item_len; /* length of bit name */
- uint32_t buf_len = 0;
- uint32_t item_pos;
- uint32_t item_pos_expected;
- char *buf = NULL;
- const char *item = NULL;
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- ly_bool item_present;
- LY_ARRAY_COUNT_TYPE item_present_index;
- LY_ARRAY_COUNT_TYPE it;
+ 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;
+ }
- *err = NULL;
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* 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 != LY_SUCCESS, cleanup_value);
+ LY_CHECK_GOTO(ret, cleanup);
- /* remember the present items for further work */
- ret = ly_set_new(&items);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup_value);
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
- /* get all values */
- while (index_end < value_len) {
- /* skip leading spaces */
- index_start = index_end;
- while ((index_start < value_len) && isspace(value_str[index_start])) {
- index_start++;
- }
-
- index_end = index_start;
- /* find end of word */
- while ((index_end < value_len) && !isspace(value_str[index_end])) {
- index_end++;
- }
-
- /* check if name of bit is valid */
- item = &value_str[index_start];
- item_len = index_end - index_start;
- if (item_len == 0) {
- /* loop read all bits names*/
- break;
- }
-
- /* looking for correct name */
- item_present = 0;
- LY_ARRAY_FOR(type_bits->bits, it) {
- if (!ly_strncmp(type_bits->bits[it].name, item, item_len)) {
- item_present = 1;
- item_present_index = it;
- }
- }
-
- /* check if name exists */
- if (!item_present) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid bit value \"%.*s\".", (int)item_len, item);
- goto cleanup;
- }
-
- /* add item to set */
- item_pos_expected = items->count;
- ret = ly_set_add(items, &type_bits->bits[item_present_index], 0, &item_pos);
- LY_CHECK_GOTO(ret, cleanup);
- if (item_pos != item_pos_expected) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Bit \"%s\" used multiple times.", type_bits->bits[item_present_index].name);
- goto cleanup;
- }
- /* count require buff size */
- buf_len += item_len + 1;
- }
-
- /* creating buffer for cannonical value */
- if (buf_len != 0) {
- uint32_t buf_index = 0;
- LY_ARRAY_COUNT_TYPE it;
-
- /* create space for cannonical value and array for bits*/
- buf = malloc(buf_len * sizeof *buf);
- if (buf == NULL) {
- ret = LY_EMEM;
- LOGMEM(ctx);
- goto cleanup;
- }
- LY_ARRAY_CREATE_GOTO(ctx, bits_items, items->count, ret, cleanup);
-
- /* generate ordered bits list and cannonical value*/
- LY_ARRAY_FOR(type_bits->bits, it) {
- if (ly_set_contains(items, &type_bits->bits[it], NULL)) {
- uint32_t name_index = 0;
-
- /* write space */
- if (buf_index != 0) {
- buf[buf_index] = ' ';
- buf_index++;
- }
-
- /* write bit name*/
- while (type_bits->bits[it].name[name_index]) {
- buf[buf_index] = type_bits->bits[it].name[name_index];
- buf_index++;
- name_index++;
- }
-
- bits_items[LY_ARRAY_COUNT(bits_items)] = &type_bits->bits[it];
- LY_ARRAY_INCREMENT(bits_items);
- }
- }
- buf[buf_index] = 0;
-
- ret = lydict_insert_zc(ctx, buf, &storage->_canonical);
- buf = NULL;
- LY_CHECK_GOTO(ret, cleanup);
- } else {
- bits_items = NULL;
- ret = lydict_insert(ctx, "", 0, &storage->_canonical);
- buf = NULL;
- LY_CHECK_GOTO(ret, cleanup);
- }
-
- /* store value */
- storage->bits_items = bits_items;
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
storage->realtype = type;
- /* RETURN LY_SUCCESS */
- ly_set_free(items, NULL);
+ /* 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((char *)value);
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_bits(ctx, storage);
+ }
+ return ret;
+}
+
+API LY_ERR
+lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_bits *v1 = val1->ptr, *v2 = val2->ptr;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (memcmp(v1->bitmap, v2->bitmap, lyplg_type_bits_bitmap_size(type_bits))) {
+ return LY_ENOT;
}
return LY_SUCCESS;
+}
- /* ERROR HANDLING */
-cleanup:
- LY_ARRAY_FREE(bits_items);
- free(buf);
- ly_set_free(items, NULL);
-cleanup_value:
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+API 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 = value->ptr;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = lyplg_type_bits_bitmap_size(type_bits);
+ }
+ return val->bitmap;
}
- return ret;
+ /* 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;
+}
+
+API const void *
+lyplg_type_hash_bits(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)value->realtype;
+ struct lyd_value_bits *val = value->ptr;
+
+ /* return the bitmap */
+ *dynamic = 0;
+ *key_len = lyplg_type_bits_bitmap_size(type_bits);
+ return val->bitmap;
}
API LY_ERR
lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
- LY_ERR ret = LY_SUCCESS;
+ LY_ERR ret;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)original->realtype;
LY_ARRAY_COUNT_TYPE u;
- struct lysc_type_bitenum_item **bits_items = NULL;
+ struct lyd_value_bits *orig_val = original->ptr, *dup_val;
- LY_ARRAY_CREATE_RET(ctx, bits_items, LY_ARRAY_COUNT(original->bits_items), LY_EMEM);
- LY_ARRAY_FOR(original->bits_items, u) {
- LY_ARRAY_INCREMENT(bits_items);
- bits_items[u] = original->bits_items[u];
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+ dup->ptr = dup_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];
}
- ret = lydict_insert(ctx, original->_canonical, strlen(original->_canonical), &dup->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- dup->bits_items = bits_items;
+ dup->ptr = dup_val;
dup->realtype = original->realtype;
return LY_SUCCESS;
- /* ERROR HANDLING */
-cleanup:
- LY_ARRAY_FREE(bits_items);
+error:
+ lyplg_type_free_bits(ctx, dup);
return ret;
}
API void
lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value)
{
- LY_ARRAY_FREE(value->bits_items);
- value->bits_items = NULL;
+ struct lyd_value_bits *val = value->ptr;
lydict_remove(ctx, value->_canonical);
- value->_canonical = NULL;
+ if (val) {
+ free(val->bitmap);
+ LY_ARRAY_FREE(val->items);
+ free(val);
+ }
}
/**
@@ -238,9 +521,9 @@
.plugin.id = "libyang 2 - bits, version 1",
.plugin.store = lyplg_type_store_bits,
.plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
+ .plugin.compare = lyplg_type_compare_bits,
+ .plugin.print = lyplg_type_print_bits,
+ .plugin.hash = lyplg_type_hash_bits,
.plugin.duplicate = lyplg_type_dup_bits,
.plugin.free = lyplg_type_free_bits
},
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
index 5a681ad..abb27d4 100644
--- a/src/plugins_types/boolean.c
+++ b/src/plugins_types/boolean.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <stdint.h>
@@ -28,21 +26,53 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBoolean boolean (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 1 | yes | `int8_t *` | 0 for false, otherwise true |
+ */
+
API LY_ERR
lyplg_type_store_boolean(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
- uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ 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;
int8_t i;
- *err = NULL;
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB boolean value size %zu (expected 1).",
+ value_len);
+ goto cleanup;
+ }
+
+ /* cast the value */
+ i = *(int8_t *)value;
+
+ /* init storage and set the value */
+ storage->_canonical = NULL;
+ storage->boolean = i ? 1 : 0;
+ storage->realtype = type;
+
+ /* store canonical value */
+ ret = lydict_insert(ctx, i ? "true" : "false", 0, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* success */
+ goto cleanup;
+ }
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
+ /* validate and get the value */
if ((value_len == ly_strlen_const("true")) && !strncmp(value, "true", ly_strlen_const("true"))) {
i = 1;
} else if ((value_len == ly_strlen_const("false")) && !strncmp(value, "false", ly_strlen_const("false"))) {
@@ -53,21 +83,29 @@
goto cleanup;
}
+ /* init storage and set the value */
+ storage->_canonical = NULL;
+ storage->boolean = i;
+ storage->realtype = type;
+
+ /* store canonical value, it always is */
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
options &= ~LYPLG_TYPE_STORE_DYNAMIC;
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
} else {
ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
}
- storage->boolean = i;
- storage->realtype = type;
cleanup:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
free((char *)value);
}
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
return ret;
}
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
index 24b5034..ec8a25f 100644
--- a/src/plugins_types/date_and_time.c
+++ b/src/plugins_types/date_and_time.c
@@ -12,16 +12,19 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
#include "plugins_types.h"
#include <arpa/inet.h>
#include <assert.h>
+#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include "libyang.h"
@@ -29,228 +32,404 @@
#include "compat.h"
/**
- * @brief Convert string to a number.
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDateAndTime date-and-time (ietf-yang-types)
*
- * @param[in,out] str String to convert, the parsed number are skipped.
- * @param[in] len Expected length of the number. 0 to parse at least 1 character.
- * @param[in] full_str Full string for error generation.
- * @param[out] num Converted number.
- * @param[out] err Error information on error.
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `time_t *` | UNIX timestamp |
+ * | string length | no | `char *` | string with the fraction digits of a second |
+ */
+
+static void lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Stored value structure for date-and-time
+ */
+struct lyd_value_date_and_time {
+ time_t time; /**< UNIX timestamp */
+ char *fractions_s; /**< fractions of a second */
+};
+
+/**
+ * @brief Convert date-and-time from string to UNIX timestamp and fractions of a second.
+ *
+ * @param[in] value Valid string value.
+ * @param[out] time UNIX timestamp.
+ * @param[out] fractions_s Fractions of a second, set to NULL if none.
* @return LY_ERR value.
*/
static LY_ERR
-convert_number(const char **str, int len, const char *full_str, int *num, struct ly_err_item **err)
+dat_str2time(const char *value, time_t *time, char **fractions_s)
{
- char *ptr;
+ struct tm tm = {0};
+ uint32_t i, frac_len;
+ const char *frac;
+ int64_t shift, shift_m;
+ time_t t;
- *num = strtoul(*str, &ptr, 10);
- if ((len && (ptr - *str != len)) || (!len && (ptr == *str))) {
- return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
- "a digit expected.", ptr[0], full_str);
+ tm.tm_year = atoi(&value[0]) - 1900;
+ tm.tm_mon = atoi(&value[5]) - 1;
+ tm.tm_mday = atoi(&value[8]);
+ tm.tm_hour = atoi(&value[11]);
+ tm.tm_min = atoi(&value[14]);
+ tm.tm_sec = atoi(&value[17]);
+
+ t = timegm(&tm);
+ i = 19;
+
+ /* fractions of a second */
+ if (value[i] == '.') {
+ ++i;
+ frac = &value[i];
+ for (frac_len = 0; isdigit(frac[frac_len]); ++frac_len) {}
+
+ i += frac_len;
+
+ /* skip trailing zeros */
+ for ( ; frac_len && (frac[frac_len - 1] == '0'); --frac_len) {}
+
+ if (!frac_len) {
+ /* only zeros, ignore */
+ frac = NULL;
+ }
+ } else {
+ frac = NULL;
}
- *str = ptr;
+ /* apply offset */
+ if ((value[i] == 'Z') || (value[i] == 'z')) {
+ /* zero shift */
+ shift = 0;
+ } else {
+ shift = strtol(&value[i], NULL, 10);
+ shift = shift * 60 * 60; /* convert from hours to seconds */
+ shift_m = strtol(&value[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
+ /* correct sign */
+ if (shift < 0) {
+ shift_m *= -1;
+ }
+ /* connect hours and minutes of the shift */
+ shift = shift + shift_m;
+ }
+
+ /* we have to shift to the opposite way to correct the time */
+ t -= shift;
+
+ *time = t;
+ if (frac) {
+ *fractions_s = strndup(frac, frac_len);
+ LY_CHECK_RET(!*fractions_s, LY_EMEM);
+ } else {
+ *fractions_s = NULL;
+ }
return LY_SUCCESS;
}
/**
- * @brief Check a single character.
+ * @brief Convert UNIX timestamp and fractions of a second into canonical date-and-time string value.
*
- * @param[in,out] str String to check, the parsed character is skipped.
- * @param[in] c Array of possible characters.
- * @param[in] full_str Full string for error generation.
- * @param[out] err Error information on error.
+ * @param[in] time UNIX timestamp.
+ * @param[in] fractions_s Fractions of a second, if any.
+ * @param[out] str Canonical string value.
* @return LY_ERR value.
*/
static LY_ERR
-check_char(const char **str, char c[], const char *full_str, struct ly_err_item **err)
+dat_time2str(time_t time, const char *fractions_s, char **str)
{
- LY_ERR ret;
- uint32_t i;
- char *exp_str;
+ struct tm tm;
+ char zoneshift[7];
+ int32_t zonediff_h, zonediff_m;
- for (i = 0; c[i]; ++i) {
- if ((*str)[0] == c[i]) {
- break;
- }
- }
- if (!c[i]) {
- /* "'c'" + (" or 'c'")* + \0 */
- exp_str = malloc(3 + (i - 1) * 7 + 1);
- sprintf(exp_str, "'%c'", c[0]);
- for (i = 1; c[i]; ++i) {
- sprintf(exp_str + strlen(exp_str), " or '%c'", c[i]);
- }
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
- "%s expected.", (*str)[0], full_str, exp_str);
- free(exp_str);
- return ret;
+ /* initialize the local timezone */
+ tzset();
+
+ /* convert */
+ if (!localtime_r(&time, &tm)) {
+ return LY_ESYS;
}
- ++(*str);
+ /* get timezone offset */
+ if (tm.tm_gmtoff == 0) {
+ /* time is Zulu (UTC) */
+ zonediff_h = 0;
+ zonediff_m = 0;
+ } else {
+ /* timezone offset */
+ zonediff_h = tm.tm_gmtoff / 60 / 60;
+ zonediff_m = tm.tm_gmtoff / 60 % 60;
+ }
+ sprintf(zoneshift, "%+03d:%02d", zonediff_h, zonediff_m);
+
+ /* print */
+ if (asprintf(str, "%04d-%02d-%02dT%02d:%02d:%02d%s%s%s",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
+ fractions_s ? "." : "", fractions_s ? fractions_s : "", zoneshift) == -1) {
+ return LY_EMEM;
+ }
+
return LY_SUCCESS;
}
/**
- * @brief Validate, canonize and store value of the ietf-yang-types date-and-time type.
- * Implementation of the ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for ietf-yang-types date-and-time type.
*/
static LY_ERR
lyplg_type_store_date_and_time(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)
+ 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 tm tm = {0}, tm2;
- int num;
- const char *val_str;
+ struct lysc_type_str *type_dat = (struct lysc_type_str *)type;
+ struct lyd_value_date_and_time *val;
+ uint32_t i;
+ char c;
- /* store as a string */
- ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
- storage, unres, err);
- LY_CHECK_RET(ret);
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- /* canonize */
- /* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})
- * 2018-03-21T09:11:05(.55785...)(Z|+02:00) */
- val_str = storage->_canonical;
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 8) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time value size %zu "
+ "(expected at least 8).", value_len);
+ goto cleanup;
+ }
+ for (i = 8; i < value_len; ++i) {
+ c = ((char *)value)[i];
+ if (!isdigit(c)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB date-and-time character '%c' "
+ "(expected a digit).", c);
+ goto cleanup;
+ }
+ }
- /* year */
- ret = convert_number(&val_str, 4, storage->_canonical, &tm.tm_year, err);
- LY_CHECK_GOTO(ret, cleanup);
- tm.tm_year -= 1900;
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
- LY_CHECK_GOTO(ret = check_char(&val_str, "-", storage->_canonical, err), cleanup);
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
- /* month */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_mon, err);
- LY_CHECK_GOTO(ret, cleanup);
- tm.tm_mon -= 1;
+ /* store timestamp */
+ memcpy(&val->time, value, sizeof val->time);
- LY_CHECK_GOTO(ret = check_char(&val_str, "-", storage->_canonical, err), cleanup);
+ /* store fractions of second */
+ if (value_len > 8) {
+ val->fractions_s = strndup(((char *)value) + 8, value_len - 8);
+ LY_CHECK_ERR_GOTO(!val->fractions_s, ret = LY_EMEM, cleanup);
+ }
- /* day */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_mday, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- LY_CHECK_GOTO(ret = check_char(&val_str, "T", storage->_canonical, err), cleanup);
-
- /* hours */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_hour, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- LY_CHECK_GOTO(ret = check_char(&val_str, ":", storage->_canonical, err), cleanup);
-
- /* minutes */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_min, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- LY_CHECK_GOTO(ret = check_char(&val_str, ":", storage->_canonical, err), cleanup);
-
- /* seconds */
- ret = convert_number(&val_str, 2, storage->_canonical, &tm.tm_sec, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* do not move the pointer */
- LY_CHECK_GOTO(ret = check_char(&val_str, ".Z+-", storage->_canonical, err), cleanup);
- --val_str;
-
- /* validate using mktime() */
- tm2 = tm;
- errno = 0;
- mktime(&tm);
- /* ENOENT is set when "/etc/localtime" is missing but the function suceeeds */
- if (errno && (errno != ENOENT)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed (%s).",
- storage->_canonical, strerror(errno));
- goto cleanup;
- }
- /* we now have correctly filled the remaining values, use them */
- memcpy(((char *)&tm2) + (6 * sizeof(int)), ((char *)&tm) + (6 * sizeof(int)), sizeof(struct tm) - (6 * sizeof(int)));
- /* back it up again */
- tm = tm2;
- /* let mktime() correct date & time with having the other values correct now */
- errno = 0;
- mktime(&tm);
- if (errno && (errno != ENOENT)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed (%s).",
- storage->_canonical, strerror(errno));
- goto cleanup;
- }
- /* detect changes in the filled values */
- if (memcmp(&tm, &tm2, 6 * sizeof(int))) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Checking date-and-time value \"%s\" failed, "
- "canonical date and time is \"%04d-%02d-%02dT%02d:%02d:%02d\".", storage->_canonical,
- tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+ /* success */
goto cleanup;
}
- /* tenth of a second */
- if (val_str[0] == '.') {
- ++val_str;
- ret = convert_number(&val_str, 0, storage->_canonical, &num, err);
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction, there can be only ASCII chars */
+ if (type_dat->length) {
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_dat->length, value_len, value, value_len, err);
LY_CHECK_GOTO(ret, cleanup);
}
- switch (val_str[0]) {
- case 'Z':
- /* done */
- ++val_str;
- break;
- case '+':
- case '-':
- /* timezone shift */
- if ((val_str[1] < '0') || (val_str[1] > '2')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
- if ((val_str[2] < '0') || ((val_str[1] == '2') && (val_str[2] > '3')) || (val_str[2] > '9')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
+ /* date-and-time pattern */
+ ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
- if (val_str[3] != ':') {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
- if ((val_str[4] < '0') || (val_str[4] > '5')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
- if ((val_str[5] < '0') || (val_str[5] > '9')) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".",
- val_str, storage->_canonical);
- goto cleanup;
- }
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
- val_str += 6;
- break;
- default:
- LY_CHECK_GOTO(ret = check_char(&val_str, "Z+-", storage->_canonical, err), cleanup);
+ /* pattern validation succeeded, convert to UNIX time and fractions of second */
+ ret = dat_str2time(value, &val->time, &val->fractions_s);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ 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);
+ }
}
- /* no other characters expected */
- if (val_str[0]) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid character '%c' in date-and-time value \"%s\", "
- "no characters expected.", val_str[0], storage->_canonical);
- goto cleanup;
- }
-
- /* validation succeeded and we do not want to change how it is stored */
-
cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
if (ret) {
- type->plugin->free(ctx, storage);
+ lyplg_type_free_date_and_time(ctx, storage);
}
return ret;
}
/**
+ * @brief Implementation of ::lyplg_type_compare_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_compare_date_and_time(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_date_and_time *v1 = val1->ptr, *v2 = val2->ptr;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ /* compare timestamp */
+ if (v1->time != v2->time) {
+ return LY_ENOT;
+ }
+
+ /* compare second fractions */
+ if ((!v1->fractions_s && !v2->fractions_s) ||
+ (v1->fractions_s && v2->fractions_s && !strcmp(v1->fractions_s, v2->fractions_s))) {
+ return LY_SUCCESS;
+ }
+ return LY_ENOT;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_print_date_and_time(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 lyd_value_date_and_time *val = value->ptr;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ if (val->fractions_s) {
+ ret = malloc(8 + strlen(val->fractions_s));
+ LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = 8 + strlen(val->fractions_s);
+ }
+ memcpy(ret, &val->time, sizeof val->time);
+ memcpy(ret + 8, val->fractions_s, strlen(val->fractions_s));
+ } else {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 8;
+ }
+ ret = (char *)&val->time;
+ }
+ return ret;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the canonical value */
+ if (dat_time2str(val->time, val->fractions_s, &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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for ietf-yang-types date-and-time type.
+ */
+static const void *
+lyplg_type_hash_date_and_time(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ struct lyd_value_date_and_time *val = value->ptr;
+
+ if (!val->fractions_s) {
+ /* we can use the timestamp */
+ *dynamic = 0;
+ *key_len = 8;
+ return &val->time;
+ }
+
+ /* simply use the (dynamic) LYB value */
+ return lyplg_type_print_date_and_time(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for ietf-yang-types date-and-time type.
+ */
+static LY_ERR
+lyplg_type_dup_date_and_time(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_date_and_time *orig_val = original->ptr, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+ dup->ptr = dup_val;
+
+ /* copy timestamp */
+ dup_val->time = orig_val->time;
+
+ /* duplicate second fractions */
+ if (orig_val->fractions_s) {
+ dup_val->fractions_s = strdup(orig_val->fractions_s);
+ LY_CHECK_ERR_GOTO(!dup_val->fractions_s, ret = LY_EMEM, error);
+ }
+
+ dup->ptr = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_date_and_time(ctx, dup);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for ietf-yang-types date-and-time type.
+ */
+static void
+lyplg_type_free_date_and_time(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_date_and_time *val = value->ptr;
+
+ lydict_remove(ctx, value->_canonical);
+ if (val) {
+ free(val->fractions_s);
+ free(val);
+ }
+}
+
+/**
* @brief Plugin information for date-and-time type implementation.
*
* Note that external plugins are supposed to use:
@@ -266,11 +445,11 @@
.plugin.id = "libyang 2 - date-and-time, version 1",
.plugin.store = lyplg_type_store_date_and_time,
.plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
+ .plugin.compare = lyplg_type_compare_date_and_time,
+ .plugin.print = lyplg_type_print_date_and_time,
+ .plugin.hash = lyplg_type_hash_date_and_time,
+ .plugin.duplicate = lyplg_type_dup_date_and_time,
+ .plugin.free = lyplg_type_free_date_and_time
},
{0}
};
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
index 54416a5..2b7ab17 100644
--- a/src/plugins_types/decimal64.c
+++ b/src/plugins_types/decimal64.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <inttypes.h>
@@ -29,71 +27,135 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
-API LY_ERR
-lyplg_type_store_decimal64(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
- uint32_t options, LY_VALUE_FORMAT UNUSED(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)
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDecimal64 decimal64 (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `int64_t *` | value represented without floating point |
+ */
+
+/**
+ * @brief Convert decimal64 number to canonical string.
+ *
+ * @param[in] num Decimal64 number stored in int64.
+ * @param[in] type Decimal64 type with fraction digits.
+ * @param[out] str Canonical string value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+decimal64_num2str(int64_t num, struct lysc_type_dec *type, char **str)
{
- LY_ERR ret = LY_SUCCESS;
- int64_t d;
- struct lysc_type_dec *type_dec = (struct lysc_type_dec *)type;
- char buf[LY_NUMBER_MAXLEN];
+ char *ret;
- *err = NULL;
+ /* allocate the value */
+ ret = calloc(1, LY_NUMBER_MAXLEN);
+ LY_CHECK_RET(!ret, LY_EMEM);
- if (!value || !((char *)value)[0] || !value_len) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty decimal64 value.");
- goto cleanup;
- }
-
- /* check hints */
- ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-
- ret = lyplg_type_parse_dec64(type_dec->fraction_digits, value, value_len, &d, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- /* prepare canonized value */
- if (d) {
- int count = sprintf(buf, "%" PRId64 " ", d);
- if (((d > 0) && ((count - 1) <= type_dec->fraction_digits)) ||
- ((count - 2) <= type_dec->fraction_digits)) {
+ if (num) {
+ int count = sprintf(ret, "%" PRId64 " ", num);
+ if (((num > 0) && ((count - 1) <= type->fraction_digits)) || ((count - 2) <= type->fraction_digits)) {
/* we have 0. value, print the value with the leading zeros
* (one for 0. and also keep the correct with of num according
* to fraction-digits value)
- * for (num<0) - extra character for '-' sign */
- count = sprintf(buf, "%0*" PRId64 " ", (d > 0) ? (type_dec->fraction_digits + 1) : (type_dec->fraction_digits + 2), d);
+ * for (num < 0) - extra character for '-' sign */
+ count = sprintf(ret, "%0*" PRId64 " ", (num > 0) ? (type->fraction_digits + 1) : (type->fraction_digits + 2), num);
}
- for (uint8_t i = type_dec->fraction_digits, j = 1; i > 0; i--) {
- if (j && (i > 1) && (buf[count - 2] == '0')) {
+ for (uint8_t i = type->fraction_digits, j = 1; i > 0; i--) {
+ if (j && (i > 1) && (ret[count - 2] == '0')) {
/* we have trailing zero to skip */
- buf[count - 1] = '\0';
+ ret[count - 1] = '\0';
} else {
j = 0;
- buf[count - 1] = buf[count - 2];
+ ret[count - 1] = ret[count - 2];
}
count--;
}
- buf[count - 1] = '.';
+ ret[count - 1] = '.';
} else {
/* zero */
- sprintf(buf, "0.0");
+ sprintf(ret, "0.0");
}
- /* range of the number */
- if (type_dec->range) {
- ret = lyplg_type_validate_range(type->basetype, type_dec->range, d, buf, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ *str = ret;
+ return LY_SUCCESS;
+}
+
+API LY_ERR
+lyplg_type_store_decimal64(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)
+{
+ struct lysc_type_dec *type_dec = (struct lysc_type_dec *)type;
+ LY_ERR ret = LY_SUCCESS;
+ int64_t num;
+ char *canon;
+
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 8) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB decimal64 value size %zu (expected 8).",
+ value_len);
+ goto cleanup;
+ }
+
+ /* we have the decimal64 number */
+ num = *(int64_t *)value;
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse decimal64 value */
+ ret = lyplg_type_parse_dec64(type_dec->fraction_digits, value, value_len, &num, err);
+ LY_CHECK_GOTO(ret, cleanup);
}
- ret = lydict_insert(ctx, buf, strlen(buf), &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- storage->dec64 = d;
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->dec64 = num;
storage->realtype = type;
+ /* we need canonical value for hash */
+ 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 */
+ ret = decimal64_num2str(num, type_dec, &canon);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store it */
+ ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ if (type_dec->range) {
+ /* check range of the number */
+ ret = lyplg_type_validate_range(type->basetype, type_dec->range, num, storage->_canonical,
+ strlen(storage->_canonical), err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
cleanup:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
}
return ret;
}
diff --git a/src/plugins_types/empty.c b/src/plugins_types/empty.c
index 915ef72..eb2306d 100644
--- a/src/plugins_types/empty.c
+++ b/src/plugins_types/empty.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <stdint.h>
@@ -27,6 +25,15 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEmpty empty (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 0 | yes | `void` | none |
+ */
+
API LY_ERR
lyplg_type_store_empty(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
@@ -35,32 +42,41 @@
{
LY_ERR ret = LY_SUCCESS;
- *err = NULL;
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
+ /* validation */
if (value_len) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty value \"%.*s\".", (int)value_len,
- (char *)value);
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty value length %zu.", value_len);
goto cleanup;
}
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
- options &= ~LYPLG_TYPE_STORE_DYNAMIC;
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- } else {
- ret = lydict_insert(ctx, "", value_len, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- }
+ /* init storage */
+ storage->_canonical = NULL;
storage->ptr = NULL;
storage->realtype = type;
+ /* 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, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
cleanup:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
}
return ret;
}
diff --git a/src/plugins_types/enumeration.c b/src/plugins_types/enumeration.c
index dadf51f..22422f4 100644
--- a/src/plugins_types/enumeration.c
+++ b/src/plugins_types/enumeration.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <stdint.h>
@@ -27,56 +25,120 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEnumeration enumeration (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `int32 *` | assigned value of the enum |
+ */
+
API LY_ERR
lyplg_type_store_enum(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
- uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ 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)
{
+ struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u;
- struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
+ ly_bool found = 0;
- *err = NULL;
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- /* check hints */
- ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB enumeration value size %zu (expected 4).",
+ value_len);
+ goto cleanup;
+ }
- /* find the matching enumeration value item */
- LY_ARRAY_FOR(type_enum->enums, u) {
- if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
- /* we have a match */
- goto match;
+ /* find the matching enumeration value item */
+ LY_ARRAY_FOR(type_enum->enums, u) {
+ if (type_enum->enums[u].value == *(int32_t *)value) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* value not found */
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value % " PRIi32 ".",
+ *(int32_t *)value);
+ goto cleanup;
+ }
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* find the matching enumeration value item */
+ LY_ARRAY_FOR(type_enum->enums, u) {
+ if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* enum not found */
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value \"%.*s\".", (int)value_len,
+ (char *)value);
+ goto cleanup;
}
}
- /* enum not found */
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value \"%.*s\".", (int)value_len,
- (char *)value);
- goto cleanup;
-match:
- /* validation done */
-
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
- options &= ~LYPLG_TYPE_STORE_DYNAMIC;
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- } else {
- ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- }
+ /* init storage */
+ storage->_canonical = NULL;
storage->enum_item = &type_enum->enums[u];
storage->realtype = type;
-cleanup:
+ /* store canonical value */
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *) value);
+ 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);
}
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
return ret;
}
+API const void *
+lyplg_type_print_enum(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)
+{
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 4;
+ }
+ return &value->enum_item->value;
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
/**
* @brief Plugin information for enumeration type implementation.
*
@@ -94,7 +156,7 @@
.plugin.store = lyplg_type_store_enum,
.plugin.validate = NULL,
.plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
+ .plugin.print = lyplg_type_print_enum,
.plugin.hash = lyplg_type_hash_simple,
.plugin.duplicate = lyplg_type_dup_simple,
.plugin.free = lyplg_type_free_simple
diff --git a/src/plugins_types/hex_string.c b/src/plugins_types/hex_string.c
deleted file mode 100644
index ab8f2fa..0000000
--- a/src/plugins_types/hex_string.c
+++ /dev/null
@@ -1,143 +0,0 @@
-/**
- * @file hex_string.c
- * @author Michal Vasko <mvasko@cesnet.cz>
- * @brief ietf-yang-types hex-string (and similar) 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
-
-#include "plugins_types.h"
-
-#include <arpa/inet.h>
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libyang.h"
-
-#include "common.h"
-#include "compat.h"
-
-/**
- * @brief Validate, canonize and store value of the ietf-yang-types hex-string type.
- * Implementation of the ::lyplg_type_store_clb.
- */
-static LY_ERR
-lyplg_type_store_hex_string(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;
- char *str;
- uint32_t i, len;
-
- /* store as a string */
- ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
- storage, unres, err);
- LY_CHECK_RET(ret);
-
- str = strdup(storage->_canonical);
- if (!str) {
- /* we can hardly allocate an error message */
- ret = LY_EMEM;
- goto cleanup;
- }
-
- len = strlen(str);
- for (i = 0; i < len; ++i) {
- if ((str[i] >= 'A') && (str[i] <= 'Z')) {
- /* make it lowercase (canonical format) */
- str[i] += 32;
- }
- }
-
- /* update the value correctly */
- lydict_remove(ctx, storage->_canonical);
- storage->_canonical = NULL;
- LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, str, &storage->_canonical), cleanup);
-
- /* validation succeeded and we do not want to change how it is stored */
-
-cleanup:
- if (ret) {
- type->plugin->free(ctx, storage);
- }
- return ret;
-}
-
-/**
- * @brief Plugin information for hex-string type implementation.
- *
- * Note that external plugins are supposed to use:
- *
- * LYPLG_TYPES = {
- */
-const struct lyplg_type_record plugins_hex_string[] = {
- {
- .module = "ietf-yang-types",
- .revision = "2013-07-15",
- .name = "phys-address",
-
- .plugin.id = "libyang 2 - phys-address, version 1",
- .plugin.store = lyplg_type_store_hex_string,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
- },
- {
- .module = "ietf-yang-types",
- .revision = "2013-07-15",
- .name = "mac-address",
-
- .plugin.id = "libyang 2 - mac-address, version 1",
- .plugin.store = lyplg_type_store_hex_string,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
- },
- {
- .module = "ietf-yang-types",
- .revision = "2013-07-15",
- .name = "hex-string",
-
- .plugin.id = "libyang 2 - hex-string, version 1",
- .plugin.store = lyplg_type_store_hex_string,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
- },
- {
- .module = "ietf-yang-types",
- .revision = "2013-07-15",
- .name = "uuid",
-
- .plugin.id = "libyang 2 - uuid, version 1",
- .plugin.store = lyplg_type_store_hex_string,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
- },
- {0}
-};
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 092d73a..a011ff6 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -12,7 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
#include "plugins_types.h"
@@ -29,101 +30,138 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
-static char *
-identityref_print(const struct lyd_value *value, LY_VALUE_FORMAT format, void *prefix_data)
-{
- char *result = NULL;
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIdentityref identityref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the identityref |
+ */
- if (asprintf(&result, "%s:%s", lyplg_type_get_prefix(value->ident->module, format, prefix_data),
- value->ident->name) == -1) {
- return NULL;
- } else {
- return result;
+/**
+ * @brief Print an identityref value in a specific format.
+ *
+ * @param[in] ident Identityref to print.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed identityref.
+ * @param[out] str_len Optional length of @p str.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_ident2str(const struct lysc_ident *ident, LY_VALUE_FORMAT format, void *prefix_data, char **str, size_t *str_len)
+{
+ int len;
+
+ len = asprintf(str, "%s:%s", lyplg_type_get_prefix(ident->module, format, prefix_data), ident->name);
+ if (len == -1) {
+ return LY_EMEM;
}
+
+ if (str_len) {
+ *str_len = (size_t)len;
+ }
+ return LY_SUCCESS;
}
-API LY_ERR
-lyplg_type_store_identityref(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 Convert a string identityref value to matching identity.
+ *
+ * @param[in] value Identityref value.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Context node for resolving the prefixes.
+ * @param[out] ident Found identity.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_str2ident(const char *value, size_t value_len, LY_VALUE_FORMAT format, void *prefix_data,
+ const struct ly_ctx *ctx, const struct lysc_node *ctx_node, struct lysc_ident **ident, struct ly_err_item **err)
{
- LY_ERR ret = LY_SUCCESS;
- struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
- const char *id_name, *prefix = value, *value_str = value;
- size_t id_len, prefix_len, str_len;
- char *str;
- const struct lys_module *mod = NULL;
+ const char *id_name, *prefix = value;
+ size_t id_len, prefix_len;
+ const struct lys_module *mod;
LY_ARRAY_COUNT_TYPE u;
- struct lysc_ident *ident = NULL, *identities, *base;
-
- *err = NULL;
-
- /* check hints */
- ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ struct lysc_ident *id, *identities;
/* locate prefix if any */
- for (prefix_len = 0; (prefix_len < value_len) && (value_str[prefix_len] != ':'); ++prefix_len) {}
+ for (prefix_len = 0; (prefix_len < value_len) && (value[prefix_len] != ':'); ++prefix_len) {}
if (prefix_len < value_len) {
- id_name = &value_str[prefix_len + 1];
+ id_name = &value[prefix_len + 1];
id_len = value_len - (prefix_len + 1);
} else {
prefix_len = 0;
- id_name = value_str;
+ id_name = value;
id_len = value_len;
}
if (!id_len) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty identityref value.");
- goto cleanup;
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty identityref value.");
}
mod = lyplg_type_identity_module(ctx, ctx_node, prefix, prefix_len, format, prefix_data);
if (!mod) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value_str);
- goto cleanup;
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - unable to map prefix to YANG schema.", (int)value_len, value);
}
+ id = NULL;
identities = mod->identities;
LY_ARRAY_FOR(identities, u) {
- ident = &identities[u]; /* shortcut */
- if (!ly_strncmp(ident->name, id_name, id_len)) {
+ if (!ly_strncmp(identities[u].name, id_name, id_len)) {
/* we have match */
+ id = &identities[u];
break;
}
}
- if (!identities || (u == LY_ARRAY_COUNT(identities))) {
+ if (!id) {
/* no match */
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Invalid identityref \"%.*s\" value - identity not found in module \"%s\".",
- (int)value_len, value_str, mod->name);
- goto cleanup;
- } else if (!mod->implemented) {
- /* non-implemented module */
- if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
- ret = lyplg_type_make_implemented((struct lys_module *)mod, NULL, unres);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- } else {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
- (int)value_len, value_str, mod->name);
- goto cleanup;
+ (int)value_len, value, mod->name);
+ }
+
+ *ident = id;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that an identityref is derived from the type base.
+ *
+ * @param[in] ident Identityref.
+ * @param[in] type Identityref type.
+ * @param[in] value String value for logging.
+ * @param[in] value_len Length of @p value.
+ * @param[out] err Error information on error.
+ */
+static LY_ERR
+identityref_check_base(const struct lysc_ident *ident, struct lysc_type_identityref *type, const char *value,
+ size_t value_len, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ size_t str_len;
+ char *str;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lysc_ident *base;
+
+ /* check that the identity matches some of the type's base identities */
+ LY_ARRAY_FOR(type->bases, u) {
+ if (!lyplg_type_identity_isderived(type->bases[u], ident)) {
+ /* we have match */
+ break;
}
}
- /* check that the identity matches some of the type's base identities */
- LY_ARRAY_FOR(type_ident->bases, u) {
- if (!lyplg_type_identity_isderived(type_ident->bases[u], ident)) {
- /* we have match */
- break;
- }
- }
- if (u == LY_ARRAY_COUNT(type_ident->bases)) {
+ /* it does not, generate a nice error */
+ if (u == LY_ARRAY_COUNT(type->bases)) {
str = NULL;
str_len = 1;
- LY_ARRAY_FOR(type_ident->bases, u) {
- base = type_ident->bases[u];
+ LY_ARRAY_FOR(type->bases, u) {
+ base = type->bases[u];
str_len += (u ? 2 : 0) + 1 + strlen(base->module->name) + 1 + strlen(base->name) + 1;
str = ly_realloc(str, str_len);
sprintf(str + (u ? strlen(str) : 0), "%s\"%s:%s\"", u ? ", " : "", base->module->name, base->name);
@@ -133,29 +171,89 @@
if (u == 1) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Invalid identityref \"%.*s\" value - identity not derived from the base %s.",
- (int)value_len, value_str, str);
+ (int)value_len, value, str);
} else {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
"Invalid identityref \"%.*s\" value - identity not derived from all the bases %s.",
- (int)value_len, value_str, str);
+ (int)value_len, value, str);
}
free(str);
- goto cleanup;
+ return ret;
}
- storage->ident = ident;
+ return LY_SUCCESS;
+}
- /* get JSON form since there is no canonical */
- str = identityref_print(storage, LY_VALUE_JSON, NULL);
- ret = lydict_insert_zc(ctx, str, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+API LY_ERR
+lyplg_type_store_identityref(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;
+ struct lysc_type_identityref *type_ident = (struct lysc_type_identityref *)type;
+ char *canon;
+ struct lysc_ident *ident;
+
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* find a matching identity */
+ ret = identityref_str2ident(value, value_len, format, prefix_data, ctx, ctx_node, &ident, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* handle identity in a non-implemented module */
+ if (!ident->module->implemented) {
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ ret = lyplg_type_make_implemented(ident->module, NULL, unres);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
+ (int)value_len, (char *)value, ident->module->name);
+ goto cleanup;
+ }
+ }
+
+ /* check that the identity is derived form all the bases */
+ ret = identityref_check_base(ident, type_ident, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ident = ident;
storage->realtype = type;
+ /* store canonical value */
+ if (format == LY_VALUE_CANON) {
+ 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 {
+ /* JSON format with prefix is the canonical one */
+ ret = identityref_ident2str(ident, LY_VALUE_JSON, NULL, &canon, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
cleanup:
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ free((void *)value);
}
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
return ret;
}
@@ -163,9 +261,9 @@
lyplg_type_print_identityref(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
void *prefix_data, ly_bool *dynamic, size_t *value_len)
{
- char *result;
+ char *ret;
- if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
if (dynamic) {
*dynamic = 0;
}
@@ -175,12 +273,12 @@
return value->_canonical;
}
- result = identityref_print(value, format, prefix_data);
- *dynamic = 1;
- if (value_len) {
- *value_len = strlen(result);
+ /* print the value in the specific format */
+ if (identityref_ident2str(value->ident, format, prefix_data, &ret, value_len)) {
+ return NULL;
}
- return result;
+ *dynamic = 1;
+ return ret;
}
API LY_ERR
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
index ced2bac..c7aa720 100644
--- a/src/plugins_types/instanceid.c
+++ b/src/plugins_types/instanceid.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <assert.h>
@@ -31,137 +29,119 @@
#include "path.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
-API const void *
-lyplg_type_print_instanceid(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
- void *prefix_data, ly_bool *dynamic, size_t *value_len)
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInstanceIdentifier instance-identifier (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier |
+ */
+
+/**
+ * @brief Convert compiled path (instance-identifier) into string.
+ *
+ * @param[in] path Compiled path.
+ * @param[in] format Value format.
+ * @param[in] prefix_data Format-specific data for resolving prefixes.
+ * @param[out] str Printed instance-identifier.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *prefix_data, char **str)
{
+ LY_ERR ret = LY_SUCCESS;
LY_ARRAY_COUNT_TYPE u, v;
- char *result = NULL;
+ char *result = NULL, quot;
+ const struct lys_module *mod = NULL;
+ ly_bool inherit_prefix, d;
+ const char *strval;
- if (!value->target) {
- /* value was not fully processed */
- return NULL;
- }
-
- if ((format == LY_VALUE_XML) || (format == LY_VALUE_SCHEMA)) {
+ switch (format) {
+ case LY_VALUE_XML:
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
/* everything is prefixed */
- LY_ARRAY_FOR(value->target, u) {
- ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(value->target[u].node->module, format, prefix_data),
- value->target[u].node->name);
- LY_ARRAY_FOR(value->target[u].predicates, v) {
- struct ly_path_predicate *pred = &value->target[u].predicates[v];
+ inherit_prefix = 0;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ /* the same prefix is inherited and skipped */
+ inherit_prefix = 1;
+ break;
+ }
- switch (value->target[u].pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
- case LY_PATH_PREDTYPE_POSITION:
- /* position predicate */
- ly_strcat(&result, "[%" PRIu64 "]", pred->position);
- break;
- case LY_PATH_PREDTYPE_LIST: {
- /* key-predicate */
- ly_bool d = 0;
- const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
- char quot = '\'';
- if (strchr(str, quot)) {
- quot = '"';
- }
- ly_strcat(&result, "[%s:%s=%c%s%c]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
- pred->key->name, quot, str, quot);
- if (d) {
- free((char *)str);
- }
- break;
- }
- case LY_PATH_PREDTYPE_LEAFLIST: {
- /* leaf-list-predicate */
- ly_bool d = 0;
- const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
- char quot = '\'';
- if (strchr(str, quot)) {
- quot = '"';
- }
- ly_strcat(&result, "[.=%c%s%c]", quot, str, quot);
- if (d) {
- free((char *)str);
- }
- break;
- }
- }
- }
+ LY_ARRAY_FOR(path, u) {
+ /* new node */
+ if (!inherit_prefix || (mod != path[u].node->module)) {
+ mod = path[u].node->module;
+ ret = ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), path[u].node->name);
+ } else {
+ ret = ly_strcat(&result, "/%s", path[u].node->name);
}
+ LY_CHECK_GOTO(ret, cleanup);
- *dynamic = 1;
- } else if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
- /* generate canonical, only the first node or the node changing module is prefixed */
- if (!value->_canonical) {
- struct lys_module *mod = NULL;
- LY_ARRAY_FOR(value->target, u) {
- if (mod != value->target[u].node->module) {
- mod = value->target[u].node->module;
- ly_strcat(&result, "/%s:%s", lyplg_type_get_prefix(mod, format, prefix_data), value->target[u].node->name);
+ /* node predicates */
+ LY_ARRAY_FOR(path[u].predicates, v) {
+ struct ly_path_predicate *pred = &path[u].predicates[v];
+
+ switch (path[u].pred_type) {
+ case LY_PATH_PREDTYPE_NONE:
+ break;
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ ret = ly_strcat(&result, "[%" PRIu64 "]", pred->position);
+ break;
+ case LY_PATH_PREDTYPE_LIST:
+ /* key-predicate */
+ strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+ &d, NULL);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ if (inherit_prefix) {
+ /* always the same prefix as the parent */
+ ret = ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, strval, quot);
} else {
- ly_strcat(&result, "/%s", value->target[u].node->name);
+ ret = ly_strcat(&result, "[%s:%s=%c%s%c]", lyplg_type_get_prefix(pred->key->module, format, prefix_data),
+ pred->key->name, quot, strval, quot);
}
- LY_ARRAY_FOR(value->target[u].predicates, v) {
- struct ly_path_predicate *pred = &value->target[u].predicates[v];
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list-predicate */
+ strval = pred->value.realtype->plugin->print(path[u].node->module->ctx, &pred->value, format, prefix_data,
+ &d, NULL);
- switch (value->target[u].pred_type) {
- case LY_PATH_PREDTYPE_NONE:
- break;
- case LY_PATH_PREDTYPE_POSITION:
- /* position predicate */
- ly_strcat(&result, "[%" PRIu64 "]", pred->position);
- break;
- case LY_PATH_PREDTYPE_LIST: {
- /* key-predicate */
- ly_bool d = 0;
- const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
- char quot = '\'';
- if (strchr(str, quot)) {
- quot = '"';
- }
- ly_strcat(&result, "[%s=%c%s%c]", pred->key->name, quot, str, quot);
- if (d) {
- free((char *)str);
- }
- break;
- }
- case LY_PATH_PREDTYPE_LEAFLIST: {
- /* leaf-list-predicate */
- ly_bool d = 0;
- const char *str = pred->value.realtype->plugin->print(ctx, &pred->value, format, prefix_data, &d, NULL);
- char quot = '\'';
- if (strchr(str, quot)) {
- quot = '"';
- }
- ly_strcat(&result, "[.=%c%s%c]", quot, str, quot);
- if (d) {
- free((char *)str);
- }
- break;
- }
- }
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
}
+ ret = ly_strcat(&result, "[.=%c%s%c]", quot, strval, quot);
+ if (d) {
+ free((char *)strval);
+ }
+ break;
}
- lydict_insert_zc(ctx, result, (const char **)&value->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
}
+ }
- /* use canonical */
- result = (char *)value->_canonical;
- if (dynamic) {
- *dynamic = 0;
- }
+cleanup:
+ if (ret) {
+ free(result);
} else {
- /* not supported format */
- return NULL;
+ *str = result;
}
-
- if (value_len) {
- *value_len = strlen(result);
- }
- return result;
+ return ret;
}
API LY_ERR
@@ -171,37 +151,54 @@
{
LY_ERR ret = LY_SUCCESS;
struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
- struct ly_path *path = NULL;
+ struct ly_path *path;
+ char *canon;
- /* init */
- *err = NULL;
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup_value);
+ LY_CHECK_GOTO(ret, cleanup);
- LY_CHECK_GOTO(ret = lyplg_type_lypath_new(ctx, value, value_len, options, format, prefix_data, ctx_node,
- unres, &path, err), cleanup);
+ /* compile instance-identifier into path */
+ ret = lyplg_type_lypath_new(ctx, value, value_len, options, format, prefix_data, ctx_node,
+ unres, &path, err);
+ LY_CHECK_GOTO(ret, cleanup);
- /* store resolved schema path and init storage */
+ /* init storage */
storage->_canonical = NULL;
storage->target = path;
- path = NULL;
storage->realtype = type;
- /* generate canonical value */
- lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+ /* store canonical value */
+ if (format == LY_VALUE_CANON) {
+ 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 {
+ /* JSON format with prefix is the canonical one */
+ ret = instanceid_path2str(path, LY_VALUE_JSON, NULL, &canon);
+ LY_CHECK_GOTO(ret, cleanup);
- /* cleanup */
-cleanup:
- lyplg_type_lypath_free(ctx, path);
-
-cleanup_value:
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
}
- if ((ret == LY_SUCCESS) && type_inst->require_instance) {
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_instanceid(ctx, storage);
+ }
+ if (!ret && type_inst->require_instance) {
/* needs to be resolved */
return LY_EINCOMPLETE;
} else {
@@ -225,14 +222,12 @@
}
/* find the target in data */
- if (!(ret = ly_path_eval(storage->target, tree, NULL))) {
- return LY_SUCCESS;
+ if ((ret = ly_path_eval(storage->target, tree, NULL))) {
+ const char *value = lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+ return ly_err_new(err, ret, LYVE_DATA, NULL, NULL, LY_ERRMSG_NOINST, value);
}
- /* error */
- const char *value = storage->realtype->plugin->print(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
-
- return ly_err_new(err, ret, LYVE_DATA, NULL, NULL, LY_ERRMSG_NOINST, value);
+ return LY_SUCCESS;
}
API LY_ERR
@@ -246,7 +241,7 @@
if (val1 == val2) {
return LY_SUCCESS;
- } else if (!val1->target || !val2->target || (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target))) {
+ } else if (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target)) {
return LY_ENOT;
}
@@ -274,7 +269,8 @@
break;
case LY_PATH_PREDTYPE_LIST:
/* key-predicate */
- if ((pred1->key != pred2->key) || ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ if ((pred1->key != pred2->key) ||
+ ((struct lysc_node_leaf *)pred1->key)->type->plugin->compare(&pred1->value, &pred2->value)) {
return LY_ENOT;
}
break;
@@ -291,20 +287,61 @@
return LY_SUCCESS;
}
+API const void *
+lyplg_type_print_instanceid(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ char *ret;
+
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+ }
+
+ /* print the value in the specific format */
+ if (instanceid_path2str(value->target, format, prefix_data, &ret)) {
+ return NULL;
+ }
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
+}
+
API LY_ERR
lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
- LY_CHECK_RET(lydict_insert(ctx, original->_canonical, strlen(original->_canonical), &dup->_canonical));
+ LY_ERR ret;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* canonical value */
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* copy path */
+ ret = ly_path_dup(ctx, original->target, &dup->target);
+ LY_CHECK_GOTO(ret, error);
+
dup->realtype = original->realtype;
- return ly_path_dup(ctx, original->target, &dup->target);
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_instanceid(ctx, dup);
+ return ret;
}
API void
lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value)
{
+ lydict_remove(ctx, value->_canonical);
ly_path_free(ctx, value->target);
- value->target = NULL;
- lyplg_type_free_simple(ctx, value);
}
/**
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
index c042e49..1cd29b3 100644
--- a/src/plugins_types/integer.c
+++ b/src/plugins_types/integer.c
@@ -12,7 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
+#define _GNU_SOURCE /* asprintf, strdup */
+#include <sys/cdefs.h>
#include "plugins_types.h"
@@ -28,54 +29,94 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @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
+};
+
+/**
+ * @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 | integer value |
+ */
+
API 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 UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ 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;
int base = 1;
- char *str;
+ char *canon;
struct lysc_type_num *type_num = (struct lysc_type_num *)type;
- /* check hints */
- ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- 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;
+ 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;
+ }
+
+ /* get the integer */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ num = *(int8_t *)value;
+ break;
+ case LY_TYPE_INT16:
+ num = *(int16_t *)value;
+ break;
+ case LY_TYPE_INT32:
+ num = *(int32_t *)value;
+ break;
+ case LY_TYPE_INT64:
+ num = *(int64_t *)value;
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ } 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);
}
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- LY_CHECK_ERR_GOTO(asprintf(&str, "%" PRId64, num) == -1, ret = LY_EMEM, cleanup);
-
- /* range of the number */
- if (type_num->range) {
- ret = lyplg_type_validate_range(type->basetype, type_num->range, num, str, err);
- LY_CHECK_ERR_GOTO(ret != LY_SUCCESS, free(str), cleanup);
- }
-
- /* store everything */
- ret = lydict_insert_zc(ctx, str, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-
+ /* init storage */
+ storage->_canonical = NULL;
/* matters for big-endian */
switch (type->basetype) {
case LY_TYPE_INT8:
@@ -95,60 +136,113 @@
}
storage->realtype = type;
+ 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, "%" PRId64, 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((char *)value);
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
}
return ret;
}
API 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 UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
+ 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;
int base = 0;
+ char *canon;
struct lysc_type_num *type_num = (struct lysc_type_num *)type;
- char *str;
- /* check hints */
- ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- 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;
+ 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;
+ }
+
+ /* get the integer */
+ switch (type->basetype) {
+ case LY_TYPE_UINT8:
+ num = *(uint8_t *)value;
+ break;
+ case LY_TYPE_UINT16:
+ num = *(uint16_t *)value;
+ break;
+ case LY_TYPE_UINT32:
+ num = *(uint32_t *)value;
+ break;
+ case LY_TYPE_UINT64:
+ num = *(uint64_t *)value;
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ goto cleanup;
+ }
+ } 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);
}
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- LY_CHECK_ERR_GOTO(asprintf(&str, "%" PRIu64, num) == -1, ret = LY_EMEM, cleanup);
-
- /* range of the number */
- if (type_num->range) {
- ret = lyplg_type_validate_range(type->basetype, type_num->range, num, str, err);
- LY_CHECK_ERR_GOTO(ret != LY_SUCCESS, free(str), cleanup);
- }
-
- /* store everything */
- ret = lydict_insert_zc(ctx, str, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
-
+ /* init storage */
+ storage->_canonical = NULL;
/* matters for big-endian */
switch (type->basetype) {
case LY_TYPE_UINT8:
@@ -168,9 +262,39 @@
}
storage->realtype = type;
+ 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((char *)value);
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
}
return ret;
}
diff --git a/src/plugins_types/ip_prefix.c b/src/plugins_types/ip_prefix.c
deleted file mode 100644
index d6f596c..0000000
--- a/src/plugins_types/ip_prefix.c
+++ /dev/null
@@ -1,307 +0,0 @@
-/**
- * @file ip_prefix.c
- * @author Michal Vasko <mvasko@cesnet.cz>
- * @brief ietf-inet-types ip-prefix 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 /* asprintf, strdup */
-#include <sys/cdefs.h>
-
-#include "plugins_types.h"
-
-#include <arpa/inet.h>
-#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
-#include <netinet/in.h>
-#include <sys/socket.h>
-#endif
-#include <assert.h>
-#include <errno.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "libyang.h"
-
-#include "common.h"
-#include "compat.h"
-
-/**
- * @brief Canonize an ipv4-prefix value.
- *
- * @param[in] ipv4_prefix Prefix to canonize.
- * @param[out] canonical Canonical format of @p ipv4_prefix.
- * @param[out] err Error structure on error.
- * @return LY_ERR value.
- */
-static LY_ERR
-canonize_ipv4_prefix(const char *ipv4_prefix, char **canonical, struct ly_err_item **err)
-{
- LY_ERR ret;
- const char *pref_str;
- char *ptr, *result = NULL;
- uint32_t pref, addr_bin, i, mask;
-
- *canonical = NULL;
- *err = NULL;
-
- pref_str = strchr(ipv4_prefix, '/');
- if (!pref_str) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv4 prefix \"%s\".", ipv4_prefix);
- goto error;
- }
-
- /* learn prefix */
- pref = strtoul(pref_str + 1, &ptr, 10);
- if (ptr[0] || (pref > 32)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv4 prefix \"%s\".", ipv4_prefix);
- goto error;
- }
-
- result = malloc(INET_ADDRSTRLEN + 3);
- if (!result) {
- ret = LY_EMEM;
- goto error;
- }
-
- /* copy just the network prefix */
- strncpy(result, ipv4_prefix, pref_str - ipv4_prefix);
- result[pref_str - ipv4_prefix] = '\0';
-
- /* convert it to binary form */
- if (inet_pton(AF_INET, result, (void *)&addr_bin) != 1) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", result);
- goto error;
- }
-
- /* zero host bits */
- mask = 0;
- for (i = 0; i < 32; ++i) {
- mask <<= 1;
- if (pref > i) {
- mask |= 1;
- }
- }
- mask = htonl(mask);
- addr_bin &= mask;
-
- /* convert back to string */
- if (!inet_ntop(AF_INET, (void *)&addr_bin, result, INET_ADDRSTRLEN)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address (%s).", strerror(errno));
- goto error;
- }
-
- /* add the prefix */
- strcat(result, pref_str);
-
- *canonical = result;
- return LY_SUCCESS;
-
-error:
- free(result);
- return ret;
-}
-
-/**
- * @brief Canonize an ipv6-prefix value.
- *
- * @param[in] ipv6_prefix Prefix to canonize.
- * @param[out] canonical Canonical format of @p ipv6_prefix.
- * @param[out] err Error structure on error.
- * @return LY_ERR value.
- */
-static LY_ERR
-canonize_ipv6_prefix(const char *ipv4_prefix, char **canonical, struct ly_err_item **err)
-{
- LY_ERR ret;
- const char *pref_str;
- char *ptr, *result = NULL;
- unsigned long int pref, i, j;
-
- union {
- struct in6_addr s;
- uint32_t a[4];
- } addr_bin;
- uint32_t mask;
-
- *canonical = NULL;
- *err = NULL;
-
- pref_str = strchr(ipv4_prefix, '/');
- if (!pref_str) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv6 prefix \"%s\".", ipv4_prefix);
- goto error;
- }
-
- /* learn prefix */
- pref = strtoul(pref_str + 1, &ptr, 10);
- if (ptr[0] || (pref > 128)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid IPv6 prefix \"%s\".", ipv4_prefix);
- goto error;
- }
-
- result = malloc(INET6_ADDRSTRLEN + 4);
- if (!result) {
- ret = LY_EMEM;
- goto error;
- }
-
- /* copy just the network prefix */
- strncpy(result, ipv4_prefix, pref_str - ipv4_prefix);
- result[pref_str - ipv4_prefix] = '\0';
-
- /* convert it to binary form */
- if (inet_pton(AF_INET6, result, (void *)&addr_bin.s) != 1) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", result);
- goto error;
- }
-
- /* zero host bits */
- for (i = 0; i < 4; ++i) {
- mask = 0;
- for (j = 0; j < 32; ++j) {
- mask <<= 1;
- if (pref > (i * 32) + j) {
- mask |= 1;
- }
- }
- mask = htonl(mask);
- addr_bin.a[i] &= mask;
- }
-
- /* convert back to string */
- if (!inet_ntop(AF_INET6, (void *)&addr_bin.s, result, INET6_ADDRSTRLEN)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address (%s).", strerror(errno));
- goto error;
- }
-
- /* add the prefix */
- strcat(result, pref_str);
-
- *canonical = result;
- return LY_SUCCESS;
-
-error:
- free(result);
- return ret;
-}
-
-/**
- * @brief Validate, canonize and store value of the ietf-inet-types ipv4-prefix type.
- * Implementation of the ::lyplg_type_store_clb.
- */
-static LY_ERR
-lyplg_type_store_ipv4_prefix(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;
- char *canonical;
-
- /* store as a string */
- ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
- storage, unres, err);
- LY_CHECK_RET(ret);
-
- /* canonize */
- ret = canonize_ipv4_prefix(storage->_canonical, &canonical, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (strcmp(canonical, storage->_canonical)) {
- /* some conversion took place, update the value */
- lydict_remove(ctx, storage->_canonical);
- storage->_canonical = NULL;
- LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, canonical, &storage->_canonical), cleanup);
- } else {
- free(canonical);
- }
-
-cleanup:
- if (ret) {
- type->plugin->free(ctx, storage);
- }
- return ret;
-}
-
-/**
- * @brief Validate, canonize and store value of the ietf-inet-types ipv6-prefix type.
- * Implementation of the ::lyplg_type_store_clb.
- */
-static LY_ERR
-lyplg_type_store_ipv6_prefix(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;
- char *canonical;
-
- /* store as a string */
- ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
- storage, unres, err);
- LY_CHECK_RET(ret);
-
- /* canonize */
- ret = canonize_ipv6_prefix(storage->_canonical, &canonical, err);
- LY_CHECK_GOTO(ret, cleanup);
-
- if (strcmp(canonical, storage->_canonical)) {
- /* some conversion took place, update the value */
- lydict_remove(ctx, storage->_canonical);
- storage->_canonical = NULL;
- LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, canonical, &storage->_canonical), cleanup);
- } else {
- free(canonical);
- }
-
-cleanup:
- if (ret) {
- type->plugin->free(ctx, storage);
- }
- return ret;
-}
-
-/**
- * @brief Plugin information for ip-prefix type implementation.
- *
- * Note that external plugins are supposed to use:
- *
- * LYPLG_TYPES = {
- */
-const struct lyplg_type_record plugins_ip_prefix[] = {
- {
- .module = "ietf-inet-types",
- .revision = "2013-07-15",
- .name = "ipv4-prefix",
-
- .plugin.id = "libyang 2 - ipv4-prefix, version 1",
- .plugin.store = lyplg_type_store_ipv4_prefix,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
- },
- {
- .module = "ietf-inet-types",
- .revision = "2013-07-15",
- .name = "ipv6-prefix",
-
- .plugin.id = "libyang 2 - ipv6-prefix, version 1",
- .plugin.store = lyplg_type_store_ipv6_prefix,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_simple,
- .plugin.print = lyplg_type_print_simple,
- .plugin.hash = lyplg_type_hash_simple,
- .plugin.duplicate = lyplg_type_dup_simple,
- .plugin.free = lyplg_type_free_simple
- },
- {0}
-};
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
new file mode 100644
index 0000000..55df218
--- /dev/null
+++ b/src/plugins_types/ipv4_address.c
@@ -0,0 +1,398 @@
+/**
+ * @file ipv4_address.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-address 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 /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4Address ipv4-address (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ * | string length | no | `char *` | IPv4 address zone string |
+ */
+
+/**
+ * @brief Stored value structure for ipv4-address
+ */
+struct lyd_value_ipv4_address {
+ struct in_addr addr;
+ const char *zone;
+};
+
+static void lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with optional zone to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in] ctx libyang context with dictionary.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] zone Ipv6 address zone in dictionary.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv4address_str2ip(const char *value, size_t value_len, uint32_t options, const struct ly_ctx *ctx,
+ struct in_addr *addr, const char **zone, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *addr_no_zone;
+ char *zone_ptr = NULL, *addr_dyn = NULL;
+ size_t zone_len;
+
+ /* store zone and get the string IPv4 address without it */
+ if ((zone_ptr = ly_strnchr(value, '%', value_len))) {
+ /* there is a zone index */
+ zone_len = value_len - (zone_ptr - value) - 1;
+ ret = lydict_insert(ctx, zone_ptr + 1, zone_len, zone);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the IP without it */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ *zone_ptr = '\0';
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, zone_ptr - value);
+ addr_no_zone = addr_dyn;
+ }
+ } else {
+ /* no zone */
+ *zone = NULL;
+
+ /* get the IP terminated with zero */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* we can use the value directly */
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, value_len);
+ addr_no_zone = addr_dyn;
+ }
+ }
+
+ /* store the IPv4 address in network-byte order */
+ if (!inet_pton(AF_INET, addr_no_zone, addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", addr_no_zone);
+ goto cleanup;
+ }
+
+ /* restore the value */
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
+ *zone_ptr = '%';
+ }
+
+cleanup:
+ free(addr_dyn);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_address(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;
+ const char *value_str = value;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv4_address *val;
+ size_t i;
+
+ /* zero storage so we can always free it */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address value size %zu "
+ "(expected at least 4).", value_len);
+ goto cleanup;
+ }
+ for (i = 4; i < value_len; ++i) {
+ if (!isalnum(value_str[i])) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address zone character 0x%x.",
+ value_str[i]);
+ goto cleanup;
+ }
+ }
+
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+
+ /* store zone, if any */
+ if (value_len > 4) {
+ ret = lydict_insert(ctx, value + 4, value_len - 4, &val->zone);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ val->zone = NULL;
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* get the network-byte order address */
+ ret = ipv4address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* 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, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv4_address(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_address(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv4_address *v1 = val1->ptr, *v2 = val2->ptr;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ /* zones are NULL or in the dictionary */
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr) || (v1->zone != v2->zone)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv4-address ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv4_address(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 lyd_value_ipv4_address *val = value->ptr;
+ size_t zone_len;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ if (!val->zone) {
+ /* address-only, const */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof val->addr;
+ }
+ return &val->addr;
+ }
+
+ /* dynamic */
+ zone_len = strlen(val->zone);
+ ret = malloc(sizeof val->addr + zone_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ memcpy(ret, &val->addr, sizeof val->addr);
+ memcpy(ret + sizeof val->addr, val->zone, zone_len);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = sizeof val->addr + zone_len;
+ }
+ return ret;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* '%' + zone */
+ zone_len = val->zone ? strlen(val->zone) + 1 : 0;
+ ret = malloc(INET_ADDRSTRLEN + zone_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv4 address in string (%s).", strerror(errno));
+ return NULL;
+ }
+
+ /* add zone */
+ if (zone_len) {
+ sprintf(ret + strlen(ret), "%%%s", val->zone);
+ }
+
+ /* 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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv4-address ietf-inet-types type.
+ */
+static const void *
+lyplg_type_hash_ipv4_address(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ /* simply use the (dynamic or const) LYB value */
+ return lyplg_type_print_ipv4_address(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv4-address ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv4_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv4_address *orig_val = original->ptr, *dup_val;
+
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_RET(ret);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ if (!dup_val) {
+ lydict_remove(ctx, dup->_canonical);
+ return LY_EMEM;
+ }
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+ ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
+ if (ret) {
+ lydict_remove(ctx, dup->_canonical);
+ free(dup_val);
+ return ret;
+ }
+
+ dup->ptr = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv4-address ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv4_address(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv4_address *val = value->ptr;
+
+ lydict_remove(ctx, value->_canonical);
+ if (val) {
+ lydict_remove(ctx, val->zone);
+ free(val);
+ }
+}
+
+/**
+ * @brief Plugin information for ipv4-address type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_address[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-address",
+
+ .plugin.id = "libyang 2 - ipv4-address, version 1",
+ .plugin.store = lyplg_type_store_ipv4_address,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv4_address,
+ .plugin.print = lyplg_type_print_ipv4_address,
+ .plugin.hash = lyplg_type_hash_ipv4_address,
+ .plugin.duplicate = lyplg_type_dup_ipv4_address,
+ .plugin.free = lyplg_type_free_ipv4_address
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_address_no_zone.c b/src/plugins_types/ipv4_address_no_zone.c
new file mode 100644
index 0000000..fe7057a
--- /dev/null
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -0,0 +1,233 @@
+/**
+ * @file ipv4_address_no_zone.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-address-no-zone 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 /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4AddressNoZone ipv4-address-no-zone (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ */
+
+/**
+ * @brief Stored value structure for ipv4-address
+ */
+struct lyd_value_ipv4_address_no_zone {
+ struct in_addr addr;
+};
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_address_no_zone(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_str *type_str = (struct lysc_type_str *)type;
+
+ /* zero storage so we can always free it */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 4) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-address-no-zone value size %zu "
+ "(expected 4).", value_len);
+ goto cleanup;
+ }
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = NULL;
+ storage->realtype = type;
+
+ /* store IP address as uint32_t value */
+ memcpy(&storage->uint32, value, 4);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = NULL;
+ storage->realtype = type;
+
+ /* we always need a dynamic value */
+ if (!(options & LYPLG_TYPE_STORE_DYNAMIC)) {
+ value = strndup(value, value_len);
+ LY_CHECK_ERR_GOTO(!value, ret = LY_EMEM, cleanup);
+
+ options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+
+ /* get the network-byte order address */
+ if (!inet_pton(AF_INET, value, &storage->uint32)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", (char *)value);
+ goto cleanup;
+ }
+
+ /* store canonical value */
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ 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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->uint32 != val2->uint32) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv4_address_no_zone(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)
+{
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 4;
+ }
+ return &value->uint32;
+ }
+
+ /* generate canonical value if not already (loaded from LYB) */
+ if (!value->_canonical) {
+ ret = malloc(INET_ADDRSTRLEN);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET, &value->uint32, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv4 address in string (%s).", strerror(errno));
+ 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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv4-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_hash_ipv4_address_no_zone(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ /* simply use the LYB value */
+ return lyplg_type_print_ipv4_address_no_zone(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Plugin information for ipv4-address-no-zone type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_address_no_zone[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-address-no-zone",
+
+ .plugin.id = "libyang 2 - ipv4-address-no-zone, version 1",
+ .plugin.store = lyplg_type_store_ipv4_address_no_zone,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv4_address_no_zone,
+ .plugin.print = lyplg_type_print_ipv4_address_no_zone,
+ .plugin.hash = lyplg_type_hash_ipv4_address_no_zone,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
new file mode 100644
index 0000000..4811e7a
--- /dev/null
+++ b/src/plugins_types/ipv4_prefix.c
@@ -0,0 +1,358 @@
+/**
+ * @file ipv4_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv4-prefix 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 /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv4Prefix ipv4-prefix (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `struct in_addr *` | IPv4 address in network-byte order |
+ * | 1 | yes | `uint8_t *` | prefix length up to 32 |
+ */
+
+/**
+ * @brief Stored value structure for ipv4-prefix
+ */
+struct lyd_value_ipv4_prefix {
+ struct in_addr addr;
+ uint8_t prefix;
+};
+
+static void lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with a prefix in string to a binary network-byte order value.
+ *
+ * @param[in] value String to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] addr Allocated address value to fill.
+ * @param[out] prefix Prefix length.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv4prefix_str2ip(const char *value, size_t value_len, struct in_addr *addr, uint8_t *prefix, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *pref_str;
+ char *mask_str = NULL;
+
+ /* it passed the pattern validation */
+ pref_str = ly_strnchr(value, '/', value_len);
+ ly_strntou8(pref_str + 1, value_len - (pref_str + 1 - value), prefix);
+
+ /* get just the network prefix */
+ mask_str = strndup(value, pref_str - value);
+ LY_CHECK_ERR_GOTO(!mask_str, ret = LY_EMEM, cleanup);
+
+ /* convert it to netword-byte order */
+ if (inet_pton(AF_INET, mask_str, addr) != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv4 address \"%s\".", mask_str);
+ goto cleanup;
+ }
+
+cleanup:
+ free(mask_str);
+ return ret;
+}
+
+/**
+ * @brief Zero host-portion of the IP address.
+ *
+ * @param[in,out] addr IP address.
+ * @param[in] prefix Prefix length.
+ */
+static void
+ipv4prefix_zero_host(struct in_addr *addr, uint8_t prefix)
+{
+ uint32_t i, mask;
+
+ /* zero host bits */
+ mask = 0;
+ for (i = 0; i < 32; ++i) {
+ mask <<= 1;
+ if (prefix > i) {
+ mask |= 1;
+ }
+ }
+ mask = htonl(mask);
+ addr->s_addr &= mask;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv4-prefix ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv4_prefix(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_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv4_prefix *val;
+
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 5) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix value size %zu (expected 5).",
+ value_len);
+ goto cleanup;
+ }
+ if (((uint8_t *)value)[4] > 32) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix prefix length %" PRIu8 ".",
+ ((uint8_t *)value)[4]);
+ goto cleanup;
+ }
+
+ /* store/allocate value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ memcpy(val, value, value_len);
+ }
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* zero host */
+ ipv4prefix_zero_host(&val->addr, val->prefix);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* get the mask in network-byte order */
+ ret = ipv4prefix_str2ip(value, value_len, &val->addr, &val->prefix, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* zero host */
+ ipv4prefix_zero_host(&val->addr, val->prefix);
+
+ 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, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv4_prefix(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv4_prefix(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv4_prefix *v1 = val1->ptr, *v2 = val2->ptr;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (memcmp(v1, v2, sizeof *v1)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static const void *
+lyplg_type_print_ipv4_prefix(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 lyd_value_ipv4_prefix *val = value->ptr;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof *val;
+ }
+ return val;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* IPv4 mask + '/' + prefix */
+ ret = malloc(INET_ADDRSTRLEN + 3);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* convert back to string */
+ if (!inet_ntop(AF_INET, &val->addr, ret, INET_ADDRSTRLEN)) {
+ free(ret);
+ return NULL;
+ }
+
+ /* add the prefix */
+ sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
+
+ /* 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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static const void *
+lyplg_type_hash_ipv4_prefix(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ struct lyd_value_ipv4_prefix *val = value->ptr;
+
+ *dynamic = 0;
+ *key_len = sizeof *val;
+ return val;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv4_prefix(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv4_prefix *orig_val = original->ptr, *dup_val;
+
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_RET(ret);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ if (!dup_val) {
+ lydict_remove(ctx, dup->_canonical);
+ return LY_EMEM;
+ }
+ memcpy(dup_val, orig_val, sizeof *orig_val);
+
+ dup->ptr = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-inet-types ipv4-prefix type.
+ */
+static void
+lyplg_type_free_ipv4_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ lydict_remove(ctx, value->_canonical);
+ free(value->ptr);
+}
+
+/**
+ * @brief Plugin information for ipv4-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv4_prefix[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv4-prefix",
+
+ .plugin.id = "libyang 2 - ipv4-prefix, version 1",
+ .plugin.store = lyplg_type_store_ipv4_prefix,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv4_prefix,
+ .plugin.print = lyplg_type_print_ipv4_prefix,
+ .plugin.hash = lyplg_type_hash_ipv4_prefix,
+ .plugin.duplicate = lyplg_type_dup_ipv4_prefix,
+ .plugin.free = lyplg_type_free_ipv4_prefix
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
index 88e00e5..98660d5 100644
--- a/src/plugins_types/ipv6_address.c
+++ b/src/plugins_types/ipv6_address.c
@@ -36,12 +36,12 @@
/**
* @page howtoDataLYB LYB Binary Format
- * @subsection howtoDataLYBTypesIPv6Address ietf-inet-types LYB ipv6-address(-no-zone)
+ * @subsection howtoDataLYBTypesIPv6Address ipv6-address (ietf-inet-types)
*
- * | Size (B) | Mandatory | Meaning |
- * | :------ | :-------: | :-----: |
- * | 16 | yes | IPv6 address in network-byte order |
- * | variable | no | IPv6 address zone string |
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | string length | no | `char *` | IPv6 address zone string |
*/
/**
@@ -52,25 +52,76 @@
const char *zone;
};
-/**
- * @brief Free an ipv6-address value.
- * Implementation of ::lyplg_type_free_clb.
- */
-static void
-lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value)
-{
- struct lyd_value_ipv6_address *val = value->ptr;
+static void lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value);
- lydict_remove(ctx, value->_canonical);
- if (val) {
- lydict_remove(ctx, val->zone);
- free(val);
+/**
+ * @brief Convert IP address with optional zone to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in] ctx libyang context with dictionary.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] zone Ipv6 address zone in dictionary.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6address_str2ip(const char *value, size_t value_len, uint32_t options, const struct ly_ctx *ctx,
+ struct in6_addr *addr, const char **zone, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *addr_no_zone;
+ char *zone_ptr = NULL, *addr_dyn = NULL;
+ size_t zone_len;
+
+ /* store zone and get the string IPv6 address without it */
+ if ((zone_ptr = ly_strnchr(value, '%', value_len))) {
+ /* there is a zone index */
+ zone_len = value_len - (zone_ptr - value) - 1;
+ ret = lydict_insert(ctx, zone_ptr + 1, zone_len, zone);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* get the IP without it */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ *zone_ptr = '\0';
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, zone_ptr - value);
+ addr_no_zone = addr_dyn;
+ }
+ } else {
+ /* no zone */
+ *zone = NULL;
+
+ /* get the IP terminated with zero */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* we can use the value directly */
+ addr_no_zone = value;
+ } else {
+ addr_dyn = strndup(value, value_len);
+ addr_no_zone = addr_dyn;
+ }
}
+
+ /* store the IPv6 address in network-byte order */
+ if (!inet_pton(AF_INET6, addr_no_zone, addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_no_zone);
+ goto cleanup;
+ }
+
+ /* restore the value */
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
+ *zone_ptr = '%';
+ }
+
+cleanup:
+ free(addr_dyn);
+ return ret;
}
/**
- * @brief Validate and store value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_store_clb.
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-address ietf-inet-types type.
*/
static LY_ERR
lyplg_type_store_ipv6_address(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
@@ -79,37 +130,31 @@
struct ly_err_item **err)
{
LY_ERR ret = LY_SUCCESS;
- const char *addr_no_zone, *value_str = value;
- char *zone_ptr = NULL, *addr_dyn = NULL;
+ const char *value_str = value;
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
struct lyd_value_ipv6_address *val;
- size_t i, zone_len;
+ size_t i;
+
+ /* zero storage so we can always free it */
+ memset(storage, 0, sizeof *storage);
if (format == LY_VALUE_LYB) {
/* validation */
- if (!strcmp(type->plugin->id, "libyang 2 - ipv6-address-no-zone, version 1")) {
- if (value_len != 16) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address-no-zone value size %zu "
- "(expected 16).", value_len);
+ if (value_len < 16) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address value size %zu "
+ "(expected at least 16).", value_len);
+ goto cleanup;
+ }
+ for (i = 16; i < value_len; ++i) {
+ if (!isalnum(value_str[i])) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address zone character 0x%x.",
+ value_str[i]);
goto cleanup;
}
- } else {
- if (value_len < 16) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address value size %zu "
- "(expected at least 16).", value_len);
- goto cleanup;
- }
- for (i = 16; i < value_len; ++i) {
- if (!isalnum(value_str[i])) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address zone character 0x%x.",
- value_str[i]);
- goto cleanup;
- }
- }
}
/* allocate the value */
- val = malloc(sizeof *val);
+ val = calloc(1, sizeof *val);
LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
/* init storage */
@@ -132,21 +177,14 @@
goto cleanup;
}
- /* zero storage so we can always free it */
- memset(storage, 0, sizeof *storage);
-
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
LY_CHECK_GOTO(ret, cleanup);
/* length restriction of the string */
if (type_str->length) {
- char buf[LY_NUMBER_MAXLEN];
- size_t char_count = ly_utf8len(value, value_len);
-
/* value_len is in bytes, but we need number of characters here */
- snprintf(buf, LY_NUMBER_MAXLEN, "%zu", char_count);
- ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, char_count, buf, err);
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
LY_CHECK_GOTO(ret, cleanup);
}
@@ -163,48 +201,9 @@
storage->ptr = val;
storage->realtype = type;
- /* store zone and get the string IPv6 address without it */
- if ((zone_ptr = ly_strnchr(value_str, '%', value_len))) {
- /* there is a zone index */
- zone_len = value_len - (zone_ptr - value_str) - 1;
- ret = lydict_insert(ctx, zone_ptr + 1, zone_len, &val->zone);
- LY_CHECK_GOTO(ret, cleanup);
-
- /* get the IP without it */
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- *zone_ptr = '\0';
- addr_no_zone = value_str;
- } else {
- addr_dyn = strndup(value_str, zone_ptr - value_str);
- addr_no_zone = addr_dyn;
- }
- } else {
- /* no zone */
- val->zone = NULL;
-
- /* get the IP terminated with zero */
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- ((char *)value_str)[value_len] = '\0';
- addr_no_zone = value_str;
- } else if (value_str[value_len] != '\0') {
- addr_dyn = strndup(value_str, value_len);
- addr_no_zone = addr_dyn;
- } else {
- /* we can use the value directly */
- addr_no_zone = value_str;
- }
- }
-
- /* store the IPv6 address in network-byte order */
- if (!inet_pton(AF_INET6, addr_no_zone, &val->addr)) {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_no_zone);
- goto cleanup;
- }
-
- /* restore the value */
- if ((options & LYPLG_TYPE_STORE_DYNAMIC) && zone_ptr) {
- *zone_ptr = '%';
- }
+ /* get the network-byte order address */
+ ret = ipv6address_str2ip(value, value_len, options, ctx, &val->addr, &val->zone, err);
+ LY_CHECK_GOTO(ret, cleanup);
if (format == LY_VALUE_CANON) {
/* store canonical value */
@@ -219,9 +218,8 @@
}
cleanup:
- free(addr_dyn);
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ free((void *)value);
}
if (ret) {
@@ -231,8 +229,7 @@
}
/**
- * @brief Compare 2 values of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_compare_clb.
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv6-address ietf-inet-types type.
*/
static LY_ERR
lyplg_type_compare_ipv6_address(const struct lyd_value *val1, const struct lyd_value *val2)
@@ -251,8 +248,7 @@
}
/**
- * @brief Print a value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_print_clb.
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv6-address ietf-inet-types type.
*/
static const void *
lyplg_type_print_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
@@ -263,15 +259,23 @@
char *ret;
if (format == LY_VALUE_LYB) {
- /* binary format is not using cache and will always be dynamic */
- zone_len = val->zone ? strlen(val->zone) : 0;
+ if (!val->zone) {
+ /* address-only, const */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof val->addr;
+ }
+ return &val->addr;
+ }
+
+ /* dynamic */
+ zone_len = strlen(val->zone);
ret = malloc(sizeof val->addr + zone_len);
LY_CHECK_RET(!ret, NULL);
memcpy(ret, &val->addr, sizeof val->addr);
- if (zone_len) {
- memcpy(ret + sizeof val->addr, val->zone, zone_len);
- }
+ memcpy(ret + sizeof val->addr, val->zone, zone_len);
+
*dynamic = 1;
if (value_len) {
*value_len = sizeof val->addr + zone_len;
@@ -316,28 +320,17 @@
}
/**
- * @brief Get hash key of a value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_hash_clb.
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv6-address ietf-inet-types type.
*/
static const void *
lyplg_type_hash_ipv6_address(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
{
- struct lyd_value_ipv6_address *val = value->ptr;
-
- if (!val->zone) {
- /* we can use the IP directly */
- *dynamic = 0;
- *key_len = sizeof val->addr;
- return &val->addr;
- }
-
- /* simply use the (dynamic) LYB value */
+ /* simply use the (dynamic or const) LYB value */
return lyplg_type_print_ipv6_address(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
}
/**
- * @brief Duplicate a value of the ietf-inet-types ipv6-address type.
- * Implementation of ::lyplg_type_dup_clb.
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv6-address ietf-inet-types type.
*/
static LY_ERR
lyplg_type_dup_ipv6_address(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
@@ -348,7 +341,7 @@
ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
LY_CHECK_RET(ret);
- dup_val = malloc(sizeof *dup_val);
+ dup_val = calloc(1, sizeof *dup_val);
if (!dup_val) {
lydict_remove(ctx, dup->_canonical);
return LY_EMEM;
@@ -367,13 +360,28 @@
}
/**
- * @brief Plugin information for ip-address type implementation.
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv6-address ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv6_address(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv6_address *val = value->ptr;
+
+ lydict_remove(ctx, value->_canonical);
+ if (val) {
+ lydict_remove(ctx, val->zone);
+ free(val);
+ }
+}
+
+/**
+ * @brief Plugin information for ipv6-address type implementation.
*
* Note that external plugins are supposed to use:
*
* LYPLG_TYPES = {
*/
-const struct lyplg_type_record plugins_ip_address[] = {
+const struct lyplg_type_record plugins_ipv6_address[] = {
{
.module = "ietf-inet-types",
.revision = "2013-07-15",
@@ -388,19 +396,5 @@
.plugin.duplicate = lyplg_type_dup_ipv6_address,
.plugin.free = lyplg_type_free_ipv6_address
},
- {
- .module = "ietf-inet-types",
- .revision = "2013-07-15",
- .name = "ipv6-address-no-zone",
-
- .plugin.id = "libyang 2 - ipv6-address-no-zone, version 1",
- .plugin.store = lyplg_type_store_ipv6_address,
- .plugin.validate = NULL,
- .plugin.compare = lyplg_type_compare_ipv6_address,
- .plugin.print = lyplg_type_print_ipv6_address,
- .plugin.hash = lyplg_type_hash_ipv6_address,
- .plugin.duplicate = lyplg_type_dup_ipv6_address,
- .plugin.free = lyplg_type_free_ipv6_address
- },
{0}
};
diff --git a/src/plugins_types/ipv6_address_no_zone.c b/src/plugins_types/ipv6_address_no_zone.c
new file mode 100644
index 0000000..da526b1
--- /dev/null
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -0,0 +1,325 @@
+/**
+ * @file ipv6_address_no_zone.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-address-no-zone 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 /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6AddressNoZone ipv6-address-no-zone (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ */
+
+/**
+ * @brief Stored value structure for ipv6-address-no-zone
+ */
+struct lyd_value_ipv6_address_no_zone {
+ struct in6_addr addr;
+};
+
+static void lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address to network-byte order.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] options Type store callback options.
+ * @param[in,out] addr Allocated value for the address.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6addressnozone_str2ip(const char *value, size_t value_len, uint32_t options, struct in6_addr *addr, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *addr_str;
+ char *addr_dyn = NULL;
+
+ /* get the IP terminated with zero */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* we can use the value directly */
+ addr_str = value;
+ } else {
+ addr_dyn = strndup(value, value_len);
+ addr_str = addr_dyn;
+ }
+
+ /* store the IPv6 address in network-byte order */
+ if (!inet_pton(AF_INET6, addr_str, addr)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", addr_str);
+ goto cleanup;
+ }
+
+cleanup:
+ free(addr_dyn);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_address_no_zone(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_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv6_address_no_zone *val;
+
+ /* zero storage so we can always free it */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 16) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-address-no-zone value size %zu "
+ "(expected 16).", value_len);
+ goto cleanup;
+ }
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = NULL;
+ storage->realtype = type;
+
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* use the value directly */
+ storage->ptr = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->ptr = val;
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* get the network-byte order address */
+ ret = ipv6addressnozone_str2ip(value, value_len, options, &val->addr, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ 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, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv6_address_no_zone(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_address_no_zone(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv6_address_no_zone *v1 = val1->ptr, *v2 = val2->ptr;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_print_ipv6_address_no_zone(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 lyd_value_ipv6_address_no_zone *val = value->ptr;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof val->addr;
+ }
+ return &val->addr;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* '%' + zone */
+ ret = malloc(INET6_ADDRSTRLEN);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* get the address in string */
+ if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+ free(ret);
+ LOGERR(ctx, LY_EVALID, "Failed to get IPv6 address in string (%s).", strerror(errno));
+ 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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static const void *
+lyplg_type_hash_ipv6_address_no_zone(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ /* simply use the LYB value */
+ return lyplg_type_print_ipv6_address_no_zone(NULL, value, LY_VALUE_LYB, NULL, dynamic, key_len);
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_address_no_zone(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv6_address_no_zone *orig_val = original->ptr, *dup_val;
+
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_RET(ret);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ if (!dup_val) {
+ lydict_remove(ctx, dup->_canonical);
+ return LY_EMEM;
+ }
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+
+ dup->ptr = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ipv6-address-no-zone ietf-inet-types type.
+ */
+static void
+lyplg_type_free_ipv6_address_no_zone(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_ipv6_address *val = value->ptr;
+
+ lydict_remove(ctx, value->_canonical);
+ free(val);
+}
+
+/**
+ * @brief Plugin information for ipv6-address-no-zone type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_address_no_zone[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-address-no-zone",
+
+ .plugin.id = "libyang 2 - ipv6-address-no-zone, version 1",
+ .plugin.store = lyplg_type_store_ipv6_address_no_zone,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv6_address_no_zone,
+ .plugin.print = lyplg_type_print_ipv6_address_no_zone,
+ .plugin.hash = lyplg_type_hash_ipv6_address_no_zone,
+ .plugin.duplicate = lyplg_type_dup_ipv6_address_no_zone,
+ .plugin.free = lyplg_type_free_ipv6_address_no_zone
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
new file mode 100644
index 0000000..dddd945
--- /dev/null
+++ b/src/plugins_types/ipv6_prefix.c
@@ -0,0 +1,360 @@
+/**
+ * @file ipv6_prefix.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-prefix 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 /* asprintf, strdup */
+#include <sys/cdefs.h>
+
+#include "plugins_types.h"
+
+#include <arpa/inet.h>
+#if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+#include <netinet/in.h>
+#include <sys/socket.h>
+#endif
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIPv6Prefix ipv6-prefix (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | 1 | yes | `uint8_t *` | prefix length up to 128 |
+ */
+
+/**
+ * @brief Stored value structure for ipv6-prefix
+ */
+struct lyd_value_ipv6_prefix {
+ struct in6_addr addr;
+ uint8_t prefix;
+};
+
+static void lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Convert IP address with a prefix in string to a binary network-byte order value.
+ *
+ * @param[in] value String to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in,out] addr Allocated address value to fill.
+ * @param[out] prefix Prefix length.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+ipv6prefix_str2ip(const char *value, size_t value_len, struct in6_addr *addr, uint8_t *prefix, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *pref_str;
+ char *mask_str = NULL;
+
+ /* it passed the pattern validation */
+ pref_str = ly_strnchr(value, '/', value_len);
+ ly_strntou8(pref_str + 1, value_len - (pref_str + 1 - value), prefix);
+
+ /* get just the network prefix */
+ mask_str = strndup(value, pref_str - value);
+ LY_CHECK_ERR_GOTO(!mask_str, ret = LY_EMEM, cleanup);
+
+ /* convert it to netword-byte order */
+ if (inet_pton(AF_INET6, mask_str, addr) != 1) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to convert IPv6 address \"%s\".", mask_str);
+ goto cleanup;
+ }
+
+cleanup:
+ free(mask_str);
+ return ret;
+}
+
+/**
+ * @brief Zero host-portion of the IP address.
+ *
+ * @param[in,out] addr IP address.
+ * @param[in] prefix Prefix length.
+ */
+static void
+ipv6prefix_zero_host(struct in6_addr *addr, uint8_t prefix)
+{
+ uint32_t i, j, mask;
+
+ /* zero host bits */
+ for (i = 0; i < 4; ++i) {
+ mask = 0;
+ for (j = 0; j < 32; ++j) {
+ mask <<= 1;
+ if (prefix > (i * 32) + j) {
+ mask |= 1;
+ }
+ }
+ mask = htonl(mask);
+ ((uint32_t *)addr->s6_addr)[i] &= mask;
+ }
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ipv6-prefix ietf-inet-types type.
+ */
+static LY_ERR
+lyplg_type_store_ipv6_prefix(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_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_ipv6_prefix *val;
+
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 17) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix value size %zu (expected 17).",
+ value_len);
+ goto cleanup;
+ }
+ if (((uint8_t *)value)[16] > 128) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix prefix length %" PRIu8 ".",
+ ((uint8_t *)value)[16]);
+ goto cleanup;
+ }
+
+ /* store/allocate value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ memcpy(val, value, value_len);
+ }
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* zero host */
+ ipv6prefix_zero_host(&val->addr, val->prefix);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate the value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = val;
+ storage->realtype = type;
+
+ /* get the mask in network-byte order */
+ ret = ipv6prefix_str2ip(value, value_len, &val->addr, &val->prefix, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* zero host */
+ ipv6prefix_zero_host(&val->addr, val->prefix);
+
+ 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, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_ipv6_prefix(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static LY_ERR
+lyplg_type_compare_ipv6_prefix(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_ipv6_prefix *v1 = val1->ptr, *v2 = val2->ptr;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (memcmp(v1, v2, sizeof *v1)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static const void *
+lyplg_type_print_ipv6_prefix(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 lyd_value_ipv6_prefix *val = value->ptr;
+ char *ret;
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof *val;
+ }
+ return val;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* IPv6 mask + '/' + prefix */
+ ret = malloc(INET6_ADDRSTRLEN + 4);
+ LY_CHECK_RET(!ret, NULL);
+
+ /* convert back to string */
+ if (!inet_ntop(AF_INET6, &val->addr, ret, INET6_ADDRSTRLEN)) {
+ free(ret);
+ return NULL;
+ }
+
+ /* add the prefix */
+ sprintf(ret + strlen(ret), "/%" PRIu8, val->prefix);
+
+ /* 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;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_hash_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static const void *
+lyplg_type_hash_ipv6_prefix(const struct lyd_value *value, ly_bool *dynamic, size_t *key_len)
+{
+ struct lyd_value_ipv6_prefix *val = value->ptr;
+
+ *dynamic = 0;
+ *key_len = sizeof *val;
+ return val;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_dup_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static LY_ERR
+lyplg_type_dup_ipv6_prefix(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv6_prefix *orig_val = original->ptr, *dup_val;
+
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
+ LY_CHECK_RET(ret);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ if (!dup_val) {
+ lydict_remove(ctx, dup->_canonical);
+ return LY_EMEM;
+ }
+ memcpy(dup_val, orig_val, sizeof *orig_val);
+
+ dup->ptr = dup_val;
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-inet-types ipv6-prefix type.
+ */
+static void
+lyplg_type_free_ipv6_prefix(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ lydict_remove(ctx, value->_canonical);
+ free(value->ptr);
+}
+
+/**
+ * @brief Plugin information for ipv6-prefix type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_ipv6_prefix[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-prefix",
+
+ .plugin.id = "libyang 2 - ipv6-prefix, version 1",
+ .plugin.store = lyplg_type_store_ipv6_prefix,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv6_prefix,
+ .plugin.print = lyplg_type_print_ipv6_prefix,
+ .plugin.hash = lyplg_type_hash_ipv6_prefix,
+ .plugin.duplicate = lyplg_type_dup_ipv6_prefix,
+ .plugin.free = lyplg_type_free_ipv6_prefix
+ },
+ {0}
+};
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
index 99baf84..10a36ea 100644
--- a/src/plugins_types/leafref.c
+++ b/src/plugins_types/leafref.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <assert.h>
@@ -28,7 +26,14 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
-const struct lyplg_type_record plugins_leafref[];
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesLeafref leafref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------: | :-------: | :--: | :-----: |
+ * | exact same format as the leafref target ||||
+ */
API LY_ERR
lyplg_type_store_leafref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
@@ -61,7 +66,7 @@
lyplg_type_validate_leafref(const struct ly_ctx *UNUSED(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;
+ LY_ERR ret;
struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
char *errmsg = NULL;
@@ -75,12 +80,11 @@
/* check leafref target existence */
if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, errmsg);
- if (errmsg != NULL) {
- free(errmsg);
- }
+ free(errmsg);
+ return ret;
}
- return ret;
+ return LY_SUCCESS;
}
API LY_ERR
@@ -111,10 +115,7 @@
API void
lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value)
{
- if (value->realtype->plugin != &plugins_leafref[0].plugin) {
- /* leafref's realtype is again leafref only in case of incomplete store */
- value->realtype->plugin->free(ctx, value);
- }
+ value->realtype->plugin->free(ctx, value);
}
/**
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
index eb05c7d..94b921e 100644
--- a/src/plugins_types/string.c
+++ b/src/plugins_types/string.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <stdint.h>
@@ -27,6 +25,15 @@
#include "compat.h"
#include "plugins_internal.h" /* LY_TYPE_*_STR */
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesString string (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
API LY_ERR
lyplg_type_store_string(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
uint32_t options, LY_VALUE_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
@@ -36,41 +43,47 @@
LY_ERR ret = LY_SUCCESS;
struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
+
/* check hints */
ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
/* length restriction of the string */
if (type_str->length) {
- char buf[LY_NUMBER_MAXLEN];
- size_t char_count = ly_utf8len(value, value_len);
-
/* value_len is in bytes, but we need number of characters here */
- snprintf(buf, LY_NUMBER_MAXLEN, "%zu", char_count);
- ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, char_count, buf, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
}
/* pattern restrictions */
ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ LY_CHECK_GOTO(ret, cleanup);
- if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
- options &= ~LYPLG_TYPE_STORE_DYNAMIC;
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- } else {
- ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
- LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
- }
+ /* init storage */
+ storage->_canonical = NULL;
storage->ptr = NULL;
storage->realtype = type;
-cleanup:
+ /* store canonical value */
if (options & LYPLG_TYPE_STORE_DYNAMIC) {
- free((char *)value);
+ 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, cleanup);
}
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
return ret;
}
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
index 4223eb6..6f1180e 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 = calloc(1, 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 = 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);
+ 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/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
index 38e7731..f1a94c3 100644
--- a/src/plugins_types/xpath1.0.c
+++ b/src/plugins_types/xpath1.0.c
@@ -12,8 +12,6 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
-#define _GNU_SOURCE
-
#include "plugins_types.h"
#include <assert.h>
@@ -31,6 +29,15 @@
#include "xpath.h"
/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesXpath10 xpath1.0 (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the XPath expression |
+ */
+
+/**
* @brief Stored value structure for xpath1.0
*/
struct lyd_value_xpath10 {
@@ -199,44 +206,76 @@
API LY_ERR
lyplg_type_store_xpath10(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)
+ uint32_t options, LY_VALUE_FORMAT format, void *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 lyd_value_xpath10 *xp_val;
- char *canonical;
+ struct lysc_type_str *type_str = (struct lysc_type_str *)type;
+ struct lyd_value_xpath10 *val;
+ char *canon;
- /* store as a string */
- ret = lyplg_type_store_string(ctx, type, value, value_len, options, format, prefix_data, hints, ctx_node,
- storage, unres, err);
- LY_CHECK_RET(ret);
+ /* clear storage */
+ memset(storage, 0, sizeof *storage);
- xp_val = calloc(1, sizeof *xp_val);
- LY_CHECK_ERR_GOTO(!xp_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
- storage->ptr = xp_val;
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* length restriction of the string */
+ if (type_str->length) {
+ /* value_len is in bytes, but we need number of characters here */
+ ret = lyplg_type_validate_range(LY_TYPE_STRING, type_str->length, ly_utf8len(value, value_len), value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* pattern restrictions */
+ ret = lyplg_type_validate_patterns(type_str->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* init storage */
+ storage->_canonical = NULL;
+ storage->ptr = NULL;
+ storage->realtype = type;
+
+ /* allocate value */
+ val = calloc(1, sizeof *val);
+ LY_CHECK_ERR_GOTO(!val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ storage->ptr = val;
/* store format-specific data and context for later prefix resolution */
- ret = lyplg_type_prefix_data_new(ctx, storage->_canonical, strlen(storage->_canonical), format, prefix_data,
- &xp_val->format, &xp_val->prefix_data);
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
LY_CHECK_GOTO(ret, cleanup);
- xp_val->ctx = ctx;
+ val->ctx = ctx;
/* parse */
- ret = lyxp_expr_parse(ctx, storage->_canonical, strlen(storage->_canonical), 1, &xp_val->exp);
+ ret = lyxp_expr_parse(ctx, value, value_len, 1, &val->exp);
LY_CHECK_GOTO(ret, cleanup);
- if (format != LY_VALUE_JSON) {
- /* generate canonical (JSON) value */
- ret = xpath10_print_value(xp_val, LY_VALUE_JSON, NULL, &canonical, err);
+ /* store canonical value */
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
+ 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 {
+ /* JSON format with prefix is the canonical one */
+ ret = xpath10_print_value(val, LY_VALUE_JSON, NULL, &canon, err);
LY_CHECK_GOTO(ret, cleanup);
- /* replace the canonical value */
- lydict_remove(ctx, storage->_canonical);
- storage->_canonical = NULL;
- LY_CHECK_GOTO(ret = lydict_insert_zc(ctx, canonical, &storage->_canonical), cleanup);
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
}
cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
if (ret) {
lyplg_type_free_xpath10(ctx, storage);
}
@@ -244,13 +283,13 @@
}
API const void *
-lyplg_type_print_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+lyplg_type_print_xpath10(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
void *prefix_data, ly_bool *dynamic, size_t *value_len)
{
- char *str_value;
+ char *ret;
struct ly_err_item *err = NULL;
- if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON)) {
+ if ((format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) || (format == LY_VALUE_LYB)) {
/* canonical */
if (dynamic) {
*dynamic = 0;
@@ -261,42 +300,45 @@
return value->_canonical;
}
- if (xpath10_print_value(value->ptr, format, prefix_data, &str_value, &err)) {
+ /* print in the specific format */
+ if (xpath10_print_value(value->ptr, format, prefix_data, &ret, &err)) {
if (err) {
- LOGVAL_ERRITEM(NULL, err);
+ LOGVAL_ERRITEM(ctx, err);
ly_err_free(err);
}
- *dynamic = 0;
return NULL;
}
*dynamic = 1;
- return str_value;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
}
API LY_ERR
lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
{
LY_ERR ret = LY_SUCCESS;
- struct lyd_value_xpath10 *xp_val;
- const struct lyd_value_xpath10 *xp_val_orig = original->ptr;
+ struct lyd_value_xpath10 *orig_val = original->ptr, *dup_val;
+ /* init dup value */
memset(dup, 0, sizeof *dup);
dup->realtype = original->realtype;
- ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ ret = lydict_insert(ctx, original->_canonical, ly_strlen(original->_canonical), &dup->_canonical);
LY_CHECK_GOTO(ret, cleanup);
- xp_val = calloc(1, sizeof *xp_val);
- LY_CHECK_ERR_GOTO(!xp_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
- xp_val->ctx = ctx;
- dup->ptr = xp_val;
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ dup_val->ctx = ctx;
+ dup->ptr = dup_val;
- ret = lyxp_expr_dup(ctx, xp_val_orig->exp, &xp_val->exp);
+ ret = lyxp_expr_dup(ctx, orig_val->exp, &dup_val->exp);
LY_CHECK_GOTO(ret, cleanup);
- xp_val->format = xp_val_orig->format;
- ret = lyplg_type_prefix_data_dup(ctx, xp_val_orig->format, xp_val_orig->prefix_data, &xp_val->prefix_data);
+ 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:
@@ -309,14 +351,14 @@
API void
lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value)
{
- struct lyd_value_xpath10 *xp_val = value->ptr;
+ struct lyd_value_xpath10 *val = value->ptr;
lydict_remove(ctx, value->_canonical);
- if (xp_val) {
- lyxp_expr_free(ctx, xp_val->exp);
- lyplg_type_prefix_data_free(xp_val->format, xp_val->prefix_data);
+ if (val) {
+ lyxp_expr_free(ctx, val->exp);
+ lyplg_type_prefix_data_free(val->format, val->prefix_data);
- free(xp_val);
+ free(val);
}
}
diff --git a/src/tree_data.h b/src/tree_data.h
index 0849197..1fcc38a 100644
--- a/src/tree_data.h
+++ b/src/tree_data.h
@@ -3,7 +3,7 @@
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libyang representation of YANG data trees.
*
- * Copyright (c) 2015 - 2019 CESNET, z.s.p.o.
+ * Copyright (c) 2015 - 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.
@@ -370,7 +370,7 @@
* support out-of-the-box (meaning that have a special type plugin). Any derived types inherit the format of its
* closest type with explicit support (up to a built-in type).
*
- * @section howtoDataLYBTypes Specific storing of particular data type values
+ * @section howtoDataLYBTypes Format of specific data type values
*/
/**
@@ -535,10 +535,11 @@
uint32_t uint32; /**< 32-bit unsigned integer */
uint64_t uint64; /**< 64-bit unsigned integer */
struct lysc_type_bitenum_item *enum_item; /**< pointer to the definition of the enumeration value */
- struct lysc_type_bitenum_item **bits_items; /**< list of set pointers to the specification of the set bits ([sized array](@ref sizedarrays)) */
+ 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
plugin's callbacks to work with the data.*/
@@ -555,15 +556,15 @@
/**
* @brief Special lyd_value structure for union.
*
- * Represents data with multiple types (union). Original value is stored in the main lyd_value:canonical_cache while
- * 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 the original value to YANG modules.
- * These prefixes are necessary to parse original value to the union's subtypes.
+ * 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). */
@@ -573,6 +574,30 @@
};
/**
+ * @brief Special lyd_value structure for bits.
+ *
+ * Note that the allocate memory is rounded to bytes. Meaning that if a type defines a bit with the highest position
+ * 18, for example, only 3 bytes will be allocated and casting to a 4-byte type will not work!
+ */
+struct lyd_value_bits {
+ char *bitmap; /**< bitmap of size ::lyplg_type_bits_bitmap_size(), if its value is
+ cast to an integer type of the corresponding size, can be used
+ directly as a bitmap */
+ struct lysc_type_bitenum_item **items; /**< list of set pointers to the specification of the set
+ bits ([sized array](@ref sizedarrays)) */
+};
+
+/**
+ * @brief Special lyd_value structure for binary.
+ *
+ * Represents an arbitrary binary value.
+ */
+struct lyd_value_binary {
+ void *data; /**< binary value itself */
+ size_t size; /**< size of the @p data value */
+};
+
+/**
* @brief Metadata structure.
*
* The structure provides information about metadata of a data element. Such attributes must map to
diff --git a/tests/utests/CMakeLists.txt b/tests/utests/CMakeLists.txt
index caa41b3..0b91121 100644
--- a/tests/utests/CMakeLists.txt
+++ b/tests/utests/CMakeLists.txt
@@ -17,6 +17,7 @@
ly_add_utest(NAME int8 SOURCES types/int8.c)
ly_add_utest(NAME string SOURCES types/string.c)
ly_add_utest(NAME bits SOURCES types/bits.c)
+ly_add_utest(NAME binary SOURCES types/binary.c)
ly_add_utest(NAME inet_types SOURCES types/inet_types.c)
ly_add_utest(NAME yang_types SOURCES types/yang_types.c)
diff --git a/tests/utests/data/test_tree_data.c b/tests/utests/data/test_tree_data.c
index f0ba388..5b4054d 100644
--- a/tests/utests/data/test_tree_data.c
+++ b/tests/utests/data/test_tree_data.c
@@ -51,6 +51,7 @@
" list nexthop {min-elements 1; key \"gateway\";"
" leaf gateway {type optional-ip-address;}"
" }"
+ " leaf-list pref {type inet:ipv6-prefix;}"
"}}";
UTEST_SETUP;
@@ -482,9 +483,11 @@
assert_int_equal(LY_SUCCESS, lyd_new_inner(NULL, mod, "cont", 0, &root));
assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='10.0.0.1']", NULL, LYD_NEW_PATH_UPDATE, NULL));
assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/nexthop[gateway='2100::1']", NULL, LYD_NEW_PATH_UPDATE, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_new_path(root, NULL, "/c:cont/pref[.='fc00::/64']", NULL, 0, NULL));
assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='10.0.0.1']", 0, NULL));
assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/nexthop[gateway='2100::1']", 0, NULL));
+ assert_int_equal(LY_SUCCESS, lyd_find_path(root, "/c:cont/pref[.='fc00::/64']", 0, NULL));
lyd_free_all(root);
}
diff --git a/tests/utests/data/test_types.c b/tests/utests/data/test_types.c
index c3164da..cddda2a 100644
--- a/tests/utests/data/test_types.c
+++ b/tests/utests/data/test_types.c
@@ -350,6 +350,7 @@
TEST_TYPE_ERROR("enums", "black", "Invalid enumeration value \"black\".", "1");
}
+#if 0
static void
test_binary(void **state)
{
@@ -403,6 +404,8 @@
TEST_TYPE_ERROR("binary", "TQ==", "This base64 value must be of length 5.", "1");
}
+#endif
+
static void
test_boolean(void **state)
{
@@ -458,9 +461,9 @@
lyd_free_all(tree);
/* invalid value */
- TEST_TYPE_ERROR("empty", "x", "Invalid empty value \"x\".", "1");
+ TEST_TYPE_ERROR("empty", "x", "Invalid empty value length 1.", "1");
- TEST_TYPE_ERROR("empty", " ", "Invalid empty value \" \".", "1");
+ TEST_TYPE_ERROR("empty", " ", "Invalid empty value length 1.", "1");
}
static void
@@ -1045,7 +1048,7 @@
/* UTEST(test_string, setup),*/
/* UTEST(test_bits, setup), */
UTEST(test_enums, setup),
- UTEST(test_binary, setup),
+ /* UTEST(test_binary, setup), */
UTEST(test_boolean, setup),
UTEST(test_empty, setup),
UTEST(test_identityref, setup),
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 1b8ed65..90ea8fd 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -318,7 +318,7 @@
CHECK_LOG_CTX("Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.", "/aa:ll");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {yang-version 1.1;namespace urn:bb;prefix bb;leaf-list ll {type empty; default x;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value \"x\".).", "Schema location /bb:ll.");
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location /bb:ll.");
assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;"
"leaf-list ll {config false;type string; default one;default two;default one;}}", LYS_IN_YANG, &mod));
@@ -1843,7 +1843,7 @@
/* invalid */
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;"
"leaf l {type empty; default x;}}", LYS_IN_YANG, NULL));
- CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value \"x\".).", "Schema location /aa:l.");
+ CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid empty value length 1.).", "Schema location /aa:l.");
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module bb {namespace urn:bb;prefix bb;typedef mytype {type empty; default x;}"
"leaf l {type mytype;}}", LYS_IN_YANG, NULL));
diff --git a/tests/utests/types/binary.c b/tests/utests/types/binary.c
new file mode 100644
index 0000000..2fbb6e4
--- /dev/null
+++ b/tests/utests/types/binary.c
@@ -0,0 +1,140 @@
+/**
+ * @file binary.c
+ * @author Michal Vaško <mvasko@cesnet.cz>
+ * @brief test for built-in binary 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"
+
+static void
+test_plugin_store(void **state)
+{
+ const char *val, *dec_val;
+ char bin_val[2];
+ struct ly_err_item *err = NULL;
+ const struct lys_module *mod;
+ struct lyd_value value = {0};
+ struct lyplg_type *type = lyplg_find(LYPLG_TYPE, "", NULL, ly_data_type2str[LY_TYPE_BINARY]);
+ struct lysc_type *lysc_type;
+ LY_ERR ly_ret;
+ const char *schema;
+
+ /* create schema. Prepare common used variables */
+ schema = MODULE_CREATE_YANG("a", "leaf l {type binary;}");
+ UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, &mod);
+ lysc_type = ((struct lysc_node_leaf *)mod->compiled->data)->type;
+
+ /* check proper type */
+ assert_string_equal("libyang 2 - binary, version 1", type->id);
+
+ /* check store XML double pad */
+ val = "YWhveQ==";
+ dec_val = "ahoy";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* single pad */
+ val = "YWhveWo=";
+ dec_val = "ahoyj";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* no pad */
+ val = "YWhveWoy";
+ dec_val = "ahoyj2";
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, dec_val, strlen(dec_val),
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, dec_val, strlen(dec_val));
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /* binary data */
+ val = "q80=";
+ bin_val[0] = 0xab;
+ bin_val[1] = 0xcd;
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ assert_int_equal(LY_SUCCESS, type->store(UTEST_LYCTX, lysc_type, bin_val, 2,
+ 0, LY_VALUE_LYB, NULL, 0, NULL, &value, NULL, &err));
+ CHECK_LYD_VALUE(value, BINARY, val, bin_val, 2);
+ assert_ptr_equal(value.realtype, lysc_type);
+ type->free(UTEST_LYCTX, &value);
+
+ /*
+ * ERROR TESTS
+ */
+ val = "q80.";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ assert_string_equal(err->msg, "Invalid Base64 character '.'.");
+ ly_err_free(err);
+
+ val = "q80";
+ err = NULL;
+ ly_ret = type->store(UTEST_LYCTX, lysc_type, val, strlen(val),
+ 0, LY_VALUE_XML, NULL, LYD_VALHINT_STRING, NULL, &value, NULL, &err);
+ assert_int_equal(LY_EVALID, ly_ret);
+ assert_string_equal(err->msg, "Base64 encoded value length must be divisible by 4.");
+ ly_err_free(err);
+}
+
+int
+main(void)
+{
+ const struct CMUnitTest tests[] = {
+ UTEST(test_plugin_store)
+ };
+
+ return cmocka_run_group_tests(tests, NULL, NULL);
+}
diff --git a/tests/utests/types/bits.c b/tests/utests/types/bits.c
index 437c9ab..7688967 100644
--- a/tests/utests/types/bits.c
+++ b/tests/utests/types/bits.c
@@ -586,13 +586,13 @@
TEST_SUCCESS_XML("T0", "\n\t", BITS, "");
TEST_ERROR_XML("T0", "twelvea");
- CHECK_LOG_CTX("Invalid bit value \"twelvea\".",
+ CHECK_LOG_CTX("Invalid bit \"twelvea\".",
"Schema location /T0:port, line number 1.");
TEST_ERROR_XML("T0", "twelve t");
- CHECK_LOG_CTX("Invalid bit value \"t\".",
+ CHECK_LOG_CTX("Invalid bit \"t\".",
"Schema location /T0:port, line number 1.");
TEST_ERROR_XML("T0", "ELEVEN");
- CHECK_LOG_CTX("Invalid bit value \"ELEVEN\".",
+ CHECK_LOG_CTX("Invalid bit \"ELEVEN\".",
"Schema location /T0:port, line number 1.");
/* empty value */
diff --git a/tests/utests/types/string.c b/tests/utests/types/string.c
index cbd52d5..749d084 100644
--- a/tests/utests/types/string.c
+++ b/tests/utests/types/string.c
@@ -741,7 +741,7 @@
TEST_SUCCESS_XML("T1", "abc-de", STRING, "abc-de");
/* ERROR LENGTH */
TEST_ERROR_XML("T1", "p4a<");
- CHECK_LOG_CTX("Unsatisfied length - string length \"4\" is not allowed.",
+ CHECK_LOG_CTX("Unsatisfied length - string \"p4a<\" length is not allowed.",
"Schema location /T1:port, line number 1.");
/* TEST DEFAULT VALUE */
@@ -785,7 +785,7 @@
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
TEST_SUCCESS_XML("T_UTF8", "€€€€€", STRING, "€€€€€");
TEST_ERROR_XML("T_UTF8", "€€€");
- CHECK_LOG_CTX("Unsatisfied length - string length \"3\" is not allowed.",
+ CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.",
"Schema location /T_UTF8:port, line number 1.");
TEST_ERROR_XML("T_UTF8", "€€€€€€€€");
CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".",
@@ -869,7 +869,7 @@
TEST_SUCCESS_JSON("T1", "abc\\n-de", STRING, "abc\n-de");
/* ERROR LENGTH */
TEST_ERROR_JSON("T1", "p4a\u042F");
- CHECK_LOG_CTX("Unsatisfied length - string length \"4\" is not allowed.",
+ CHECK_LOG_CTX("Unsatisfied length - string \"p4aЯ\" length is not allowed.",
"Schema location /T1:port, line number 1.");
/* TEST DEFAULT VALUE */
@@ -907,7 +907,7 @@
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
TEST_SUCCESS_JSON("T_UTF8", "€€€€€", STRING, "€€€€€");
TEST_ERROR_JSON("T_UTF8", "€€€");
- CHECK_LOG_CTX("Unsatisfied length - string length \"3\" is not allowed.",
+ CHECK_LOG_CTX("Unsatisfied length - string \"€€€\" length is not allowed.",
"Schema location /T_UTF8:port, line number 1.");
TEST_ERROR_JSON("T_UTF8", "€€€€€€€€");
CHECK_LOG_CTX("Unsatisfied pattern - \"€€€€€€€€\" does not conform to \"[€]{5,7}\".",
@@ -977,7 +977,7 @@
"yang:operation=\"replace\" yang:orig-default=\"false\" yang:orig-value=\" 555 555\">"
"121</port>";
CHECK_PARSE_LYD_PARAM(diff_expected, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, model_1);
- CHECK_LOG_CTX("Unsatisfied length - string length \"3\" is not allowed.",
+ CHECK_LOG_CTX("Unsatisfied length - string \"121\" length is not allowed.",
"Schema location /T_DIFF:port, line number 1.");
/* diff from default value */
diff --git a/tests/utests/types/yang_types.c b/tests/utests/types/yang_types.c
index 491c22e..905ab16 100644
--- a/tests/utests/types/yang_types.c
+++ b/tests/utests/types/yang_types.c
@@ -16,7 +16,8 @@
#define _UTEST_MAIN_
#include "../utests.h"
-/* LOCAL INCLUDE HEADERS */
+#include <stdlib.h>
+
#include "libyang.h"
#define MODULE_CREATE_YIN(MOD_NAME, NODES) \
@@ -66,59 +67,41 @@
/* xml test */
schema = MODULE_CREATE_YANG("a",
"leaf l {type yang:date-and-time;}"
- "leaf l2 {type yang:phys-address;}"
- "leaf l3 {type yang:mac-address;}"
- "leaf l4 {type yang:hex-string;}"
- "leaf l5 {type yang:uuid;}"
- "leaf l6 {type yang:xpath1.0;}");
+ "leaf l2 {type yang:xpath1.0;}");
UTEST_ADD_MODULE(schema, LYS_IN_YANG, NULL, NULL);
/* date-and-time */
- TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T23:15:15.88888Z");
- TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-05-31T23:15:15-08:59");
- TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-05-31T23:15:15-23:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-25T23:15:15.88888Z", STRING, "2005-05-25T21:15:15.88888-02:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-08:59", STRING, "2005-06-01T06:14:15-02:00");
+ TEST_SUCCESS_XML("a", "l", "2005-05-31T23:15:15-23:00", STRING, "2005-06-01T20:15:15-02:00");
/* test 1 second before epoch (mktime returns -1, but it is a correct value), with and without DST */
- TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59Z", STRING, "1970-01-01T00:59:59Z");
- TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59Z", STRING, "1969-12-31T23:59:59Z");
+ TEST_SUCCESS_XML("a", "l", "1970-01-01T00:59:59-02:00", STRING, "1970-01-01T00:59:59-02:00");
+ TEST_SUCCESS_XML("a", "l", "1969-12-31T23:59:59-02:00", STRING, "1969-12-31T23:59:59-02:00");
+
+ /* canonize */
+ TEST_SUCCESS_XML("a", "l", "2005-02-29T23:15:15-02:00", STRING, "2005-03-01T23:15:15-02:00");
TEST_ERROR_XML("a", "l", "2005-05-31T23:15:15.-08:00");
CHECK_LOG_CTX("Unsatisfied pattern - \"2005-05-31T23:15:15.-08:00\" does not conform to "
"\"\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[\\+\\-]\\d{2}:\\d{2})\".",
"Schema location /a:l, line number 1.");
- TEST_ERROR_XML("a", "l", "2005-02-29T23:15:15-08:00");
- CHECK_LOG_CTX("Checking date-and-time value \"2005-02-29T23:15:15-08:00\" failed, "
- "canonical date and time is \"2005-03-01T23:15:15\".",
- "Schema location /a:l, line number 1.");
-
- /* phys-address */
- TEST_SUCCESS_XML("a", "l2", "aa:bb:cc:dd", STRING, "aa:bb:cc:dd");
- TEST_SUCCESS_XML("a", "l2", "AA:BB:1D:2F:CA:52", STRING, "aa:bb:1d:2f:ca:52");
-
- /* mac-address */
- TEST_SUCCESS_XML("a", "l3", "12:34:56:78:9A:BC", STRING, "12:34:56:78:9a:bc");
-
- /* hex-string */
- TEST_SUCCESS_XML("a", "l4", "AB:CD:eF:fE:dc:Ba:Ab", STRING, "ab:cd:ef:fe:dc:ba:ab");
-
- /* uuid */
- TEST_SUCCESS_XML("a", "l5", "12AbCDef-3456-58cd-9ABC-8796cdACdfEE", STRING, "12abcdef-3456-58cd-9abc-8796cdacdfee");
/* xpath1.0 */
- TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l6", "/aa:l6[. = '4']", STRING, "/a:l6[. = '4']");
+ TEST_SUCCESS_XML("a\" xmlns:aa=\"urn:tests:a", "l2", "/aa:l2[. = '4']", STRING, "/a:l2[. = '4']");
TEST_SUCCESS_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library\" "
- "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l6",
+ "xmlns:ds=\"urn:ietf:params:xml:ns:yang:ietf-datastores", "l2",
"/yl:yang-library/yl:datastore/yl:name = 'ds:running'", STRING,
"/ietf-yang-library:yang-library/ietf-yang-library:datastore/ietf-yang-library:name = 'ietf-datastores:running'");
- TEST_SUCCESS_XML("a", "l6", "/l6[. = '4']", STRING, "/l6[. = '4']");
+ TEST_SUCCESS_XML("a", "l2", "/l2[. = '4']", STRING, "/l2[. = '4']");
- TEST_ERROR_XML("a", "l6", "/a:l6[. = '4']");
- CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location /a:l6, line number 1.");
- TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l6",
+ TEST_ERROR_XML("a", "l2", "/a:l2[. = '4']");
+ CHECK_LOG_CTX("Failed to resolve prefix \"a\".", "Schema location /a:l2, line number 1.");
+ TEST_ERROR_XML("a\" xmlns:yl=\"urn:ietf:params:xml:ns:yang:ietf-yang-library", "l2",
"/yl:yang-library/yl:datastore/yl::name");
- CHECK_LOG_CTX("Storing value \"/yl:yang-library/yl:datastore/yl::name\" failed.", "Schema location /a:l6, line number 1.",
+ CHECK_LOG_CTX("Storing value \"/yl:yang-library/yl:datastore/yl::name\" failed.", "Schema location /a:l2, line number 1.",
"Invalid character ':'[34] of expression '/yl:yang-library/yl:datastore/yl::name'.",
- "Schema location /a:l6, line number 1.");
+ "Schema location /a:l2, line number 1.");
}
static void
diff --git a/tests/utests/utests.h b/tests/utests/utests.h
index 9be12c3..76f782c 100644
--- a/tests/utests/utests.h
+++ b/tests/utests/utests.h
@@ -21,6 +21,7 @@
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
+#include <stdlib.h>
#include <cmocka.h>
@@ -94,6 +95,8 @@
struct ly_in *in; /**< Input handler */
struct ly_out *out; /**< Outpu handler */
+
+ char *orig_tz; /**< Original "TZ" environment variable value */
};
/**
@@ -929,9 +932,9 @@
{ \
const char *arr[] = { __VA_ARGS__ }; \
LY_ARRAY_COUNT_TYPE arr_size = sizeof(arr) / sizeof(arr[0]); \
- assert_int_equal(arr_size, LY_ARRAY_COUNT((NODE).bits_items)); \
+ assert_int_equal(arr_size, LY_ARRAY_COUNT(((struct lyd_value_bits *)(NODE).ptr)->items)); \
for (LY_ARRAY_COUNT_TYPE it = 0; it < arr_size; it++) { \
- assert_string_equal(arr[it], (NODE).bits_items[it]->name); \
+ assert_string_equal(arr[it], ((struct lyd_value_bits *)(NODE).ptr)->items[it]->name); \
} \
}
@@ -1069,8 +1072,12 @@
* Example CHECK_LYD_VALUE(node->value, BINARY, "aGVs\nbG8=");
* @param[in] NODE lyd_value variable
* @param[in] CANNONICAL_VAL expected cannonical value
+ * @param[in] VALUE expected value data
+ * @param[in] SIZE expected value data size
*/
-#define CHECK_LYD_VALUE_BINARY(NODE, CANNONICAL_VAL) \
+#define CHECK_LYD_VALUE_BINARY(NODE, CANNONICAL_VAL, VALUE, SIZE) \
+ assert_int_equal((NODE).bin->size, SIZE); \
+ assert_int_equal(0, memcmp((NODE).bin->data, VALUE, SIZE)); \
assert_non_null((NODE).realtype->plugin->print(UTEST_LYCTX, &(NODE), LY_VALUE_CANON, NULL, NULL, NULL)); \
assert_string_equal((NODE)._canonical, CANNONICAL_VAL); \
assert_non_null((NODE).realtype); \
@@ -1262,6 +1269,8 @@
static int
utest_setup(void **state)
{
+ char *cur_tz;
+
/* setup the logger */
ly_set_log_clb(_utest_logger, 1);
ly_log_options(LY_LOLOG | LY_LOSTORE);
@@ -1276,6 +1285,15 @@
/* clean all errors from the setup - usually warnings regarding the plugins directories */
UTEST_LOG_CLEAN;
+ /* backup timezone, if any */
+ cur_tz = getenv("TZ");
+ if (cur_tz) {
+ current_utest_context->orig_tz = strdup(cur_tz);
+ }
+
+ /* set CET */
+ setenv("TZ", "CET+02:00", 1);
+
return 0;
}
@@ -1298,10 +1316,16 @@
/* libyang context */
ly_ctx_destroy(current_utest_context->ctx);
+ if (current_utest_context->orig_tz) {
+ /* restore TZ */
+ setenv("TZ", current_utest_context->orig_tz, 1);
+ }
+
/* utest context */
ly_in_free(current_utest_context->in, 0);
free(current_utest_context->err_msg);
free(current_utest_context->err_path);
+ free(current_utest_context->orig_tz);
free(current_utest_context);
current_utest_context = NULL;