summaryrefslogtreecommitdiffstats
path: root/src/plugins_types
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/plugins_types.c1043
-rw-r--r--src/plugins_types.h1214
-rw-r--r--src/plugins_types/binary.c466
-rw-r--r--src/plugins_types/bits.c510
-rw-r--r--src/plugins_types/boolean.c165
-rw-r--r--src/plugins_types/date_and_time.c339
-rw-r--r--src/plugins_types/decimal64.c239
-rw-r--r--src/plugins_types/empty.c103
-rw-r--r--src/plugins_types/enumeration.c202
-rw-r--r--src/plugins_types/identityref.c352
-rw-r--r--src/plugins_types/instanceid.c382
-rw-r--r--src/plugins_types/instanceid_keys.c229
-rw-r--r--src/plugins_types/integer.c585
-rw-r--r--src/plugins_types/ipv4_address.c377
-rw-r--r--src/plugins_types/ipv4_address_no_zone.c221
-rw-r--r--src/plugins_types/ipv4_prefix.c337
-rw-r--r--src/plugins_types/ipv6_address.c378
-rw-r--r--src/plugins_types/ipv6_address_no_zone.c312
-rw-r--r--src/plugins_types/ipv6_prefix.c351
-rw-r--r--src/plugins_types/leafref.c140
-rw-r--r--src/plugins_types/node_instanceid.c320
-rw-r--r--src/plugins_types/string.c109
-rw-r--r--src/plugins_types/union.c585
-rw-r--r--src/plugins_types/xpath1.0.c521
24 files changed, 9480 insertions, 0 deletions
diff --git a/src/plugins_types.c b/src/plugins_types.c
new file mode 100644
index 0000000..cb4b896
--- /dev/null
+++ b/src/plugins_types.c
@@ -0,0 +1,1043 @@
+/**
+ * @file plugins_types.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Built-in types plugins and interface for user types plugins.
+ *
+ * Copyright (c) 2019 - 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "path.h"
+#include "schema_compile.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_data.h"
+#include "tree_data_internal.h"
+#include "tree_schema.h"
+#include "tree_schema_internal.h"
+#include "xml.h"
+#include "xpath.h"
+
+/**
+ * @brief Find import prefix in imports.
+ */
+static const struct lys_module *
+ly_schema_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len, const void *prefix_data)
+{
+ const struct lysp_module *prefix_mod = prefix_data;
+ struct lys_module *m = NULL;
+ LY_ARRAY_COUNT_TYPE u;
+ const char *local_prefix;
+
+ local_prefix = prefix_mod->is_submod ? ((struct lysp_submodule *)prefix_mod)->prefix : prefix_mod->mod->prefix;
+ if (!prefix_len || !ly_strncmp(local_prefix, prefix, prefix_len)) {
+ /* it is the prefix of the module itself */
+ m = prefix_mod->mod;
+ }
+
+ /* search in imports */
+ if (!m) {
+ LY_ARRAY_FOR(prefix_mod->imports, u) {
+ if (!ly_strncmp(prefix_mod->imports[u].prefix, prefix, prefix_len)) {
+ m = prefix_mod->imports[u].module;
+ break;
+ }
+ }
+ }
+
+ return m;
+}
+
+/**
+ * @brief Find resolved module for a prefix in prefix - module pairs.
+ */
+static const struct lys_module *
+ly_schema_resolved_resolve_prefix(const struct ly_ctx *UNUSED(ctx), const char *prefix, size_t prefix_len,
+ const void *prefix_data)
+{
+ const struct lysc_prefix *prefixes = prefix_data;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(prefixes, u) {
+ if ((!prefixes[u].prefix && !prefix_len) || (prefixes[u].prefix && !ly_strncmp(prefixes[u].prefix, prefix, prefix_len))) {
+ return prefixes[u].mod;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find XML namespace prefix in XML namespaces, which are then mapped to modules.
+ */
+static const struct lys_module *
+ly_xml_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, const void *prefix_data)
+{
+ const struct lys_module *mod;
+ const struct lyxml_ns *ns;
+ const struct ly_set *ns_set = prefix_data;
+
+ ns = lyxml_ns_get(ns_set, prefix, prefix_len);
+ if (!ns) {
+ return NULL;
+ }
+
+ mod = ly_ctx_get_module_implemented_ns(ctx, ns->uri);
+ if (!mod) {
+ /* for YIN extension prefix resolution */
+ mod = ly_ctx_get_module_latest_ns(ctx, ns->uri);
+ }
+ return mod;
+}
+
+/**
+ * @brief Find module name.
+ */
+static const struct lys_module *
+ly_json_resolve_prefix(const struct ly_ctx *ctx, const char *prefix, size_t prefix_len, const void *UNUSED(prefix_data))
+{
+ return ly_ctx_get_module_implemented2(ctx, prefix, prefix_len);
+}
+
+const struct lys_module *
+ly_resolve_prefix(const struct ly_ctx *ctx, const void *prefix, size_t prefix_len, LY_VALUE_FORMAT format,
+ const void *prefix_data)
+{
+ const struct lys_module *mod = NULL;
+
+ LY_CHECK_ARG_RET(ctx, prefix, prefix_len, NULL);
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ mod = ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ case LY_VALUE_SCHEMA_RESOLVED:
+ mod = ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ mod = ly_xml_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ mod = ly_json_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ break;
+ }
+
+ return mod;
+}
+
+LIBYANG_API_DEF const struct lys_module *
+lyplg_type_identity_module(const struct ly_ctx *ctx, const struct lysc_node *ctx_node, const char *prefix,
+ size_t prefix_len, LY_VALUE_FORMAT format, const void *prefix_data)
+{
+ if (prefix_len) {
+ return ly_resolve_prefix(ctx, prefix, prefix_len, format, prefix_data);
+ } else {
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ /* use local module */
+ return ly_schema_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* use local module */
+ return ly_schema_resolved_resolve_prefix(ctx, prefix, prefix_len, prefix_data);
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* use context node module (as specified) */
+ return ctx_node ? ctx_node->module : NULL;
+ case LY_VALUE_XML:
+ /* use the default namespace */
+ return ly_xml_resolve_prefix(ctx, NULL, 0, prefix_data);
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find module in import prefixes.
+ */
+static const char *
+ly_schema_get_prefix(const struct lys_module *mod, void *prefix_data)
+{
+ const struct lysp_module *pmod = prefix_data;
+ LY_ARRAY_COUNT_TYPE u;
+
+ if (pmod->mod == mod) {
+ if (pmod->is_submod) {
+ return ((struct lysp_submodule *)pmod)->prefix;
+ } else {
+ return pmod->mod->prefix;
+ }
+ }
+
+ LY_ARRAY_FOR(pmod->imports, u) {
+ if (pmod->imports[u].module == mod) {
+ /* match */
+ return pmod->imports[u].prefix;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Find prefix in prefix - module pairs.
+ */
+static const char *
+ly_schema_resolved_get_prefix(const struct lys_module *mod, void *prefix_data)
+{
+ struct lysc_prefix *prefixes = prefix_data;
+ LY_ARRAY_COUNT_TYPE u;
+
+ LY_ARRAY_FOR(prefixes, u) {
+ if (prefixes[u].mod == mod) {
+ return prefixes[u].prefix;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Simply return module local prefix. Also, store the module in a set.
+ */
+static const char *
+ly_xml_get_prefix(const struct lys_module *mod, void *prefix_data)
+{
+ struct ly_set *ns_list = prefix_data;
+
+ LY_CHECK_RET(ly_set_add(ns_list, (void *)mod, 0, NULL), NULL);
+ return mod->prefix;
+}
+
+/**
+ * @brief Simply return module name.
+ */
+static const char *
+ly_json_get_prefix(const struct lys_module *mod, void *UNUSED(prefix_data))
+{
+ return mod->name;
+}
+
+const char *
+ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data)
+{
+ const char *prefix = NULL;
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ prefix = ly_schema_get_prefix(mod, prefix_data);
+ break;
+ case LY_VALUE_SCHEMA_RESOLVED:
+ prefix = ly_schema_resolved_get_prefix(mod, prefix_data);
+ break;
+ case LY_VALUE_XML:
+ case LY_VALUE_STR_NS:
+ prefix = ly_xml_get_prefix(mod, prefix_data);
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ prefix = ly_json_get_prefix(mod, prefix_data);
+ break;
+ }
+
+ return prefix;
+}
+
+LIBYANG_API_DEF const char *
+lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data)
+{
+ return ly_get_prefix(mod, format, prefix_data);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->_canonical == val2->_canonical) {
+ return LY_SUCCESS;
+ }
+
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_simple(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT UNUSED(format),
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = ly_strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ memset(dup, 0, sizeof *dup);
+ LY_CHECK_RET(lydict_insert(ctx, original->_canonical, 0, &dup->_canonical));
+ memcpy(dup->fixed_mem, original->fixed_mem, sizeof dup->fixed_mem);
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value, size_t value_len,
+ int64_t *ret, struct ly_err_item **err)
+{
+ LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+
+ *err = NULL;
+
+ /* consume leading whitespaces */
+ for ( ; value_len && isspace(*value); ++value, --value_len) {}
+
+ if (!value || !value_len || !value[0]) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype);
+ }
+
+ switch (ly_parse_int(value, value_len, min, max, base, ret)) {
+ case LY_EDENIED:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype);
+ case LY_SUCCESS:
+ return LY_SUCCESS;
+ default:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid type %s value \"%.*s\".", datatype, (int)value_len, value);
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value, size_t value_len, uint64_t *ret,
+ struct ly_err_item **err)
+{
+ LY_CHECK_ARG_RET(NULL, err, datatype, LY_EINVAL);
+
+ *err = NULL;
+
+ /* consume leading whitespaces */
+ for ( ; value_len && isspace(*value); ++value, --value_len) {}
+
+ if (!value || !value_len || !value[0]) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid type %s empty value.", datatype);
+ }
+
+ *err = NULL;
+ switch (ly_parse_uint(value, value_len, max, base, ret)) {
+ case LY_EDENIED:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Value \"%.*s\" is out of type %s min/max bounds.", (int)value_len, value, datatype);
+ case LY_SUCCESS:
+ return LY_SUCCESS;
+ default:
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid type %s value \"%.*s\".", datatype, (int)value_len, value);
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret, struct ly_err_item **err)
+{
+ LY_ERR ret_val;
+ char *valcopy = NULL;
+ size_t fraction = 0, size, len = 0, trailing_zeros;
+ int64_t d;
+
+ *err = NULL;
+
+ /* consume leading whitespaces */
+ for ( ; value_len && isspace(*value); ++value, --value_len) {}
+
+ /* parse value */
+ if (!value_len) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty decimal64 value.");
+ } else if (!isdigit(value[len]) && (value[len] != '-') && (value[len] != '+')) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid %zu. character of decimal64 value \"%.*s\".",
+ len + 1, (int)value_len, value);
+ }
+
+ if ((value[len] == '-') || (value[len] == '+')) {
+ ++len;
+ }
+
+ while (len < value_len && isdigit(value[len])) {
+ ++len;
+ }
+
+ trailing_zeros = 0;
+ if ((len < value_len) && ((value[len] != '.') || !isdigit(value[len + 1]))) {
+ goto decimal;
+ }
+ fraction = len;
+ ++len;
+ while (len < value_len && isdigit(value[len])) {
+ if (value[len] == '0') {
+ ++trailing_zeros;
+ } else {
+ trailing_zeros = 0;
+ }
+ ++len;
+ }
+ len = len - trailing_zeros;
+
+decimal:
+ if (fraction && (len - 1 - fraction > fraction_digits)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Value \"%.*s\" of decimal64 type exceeds defined number (%u) of fraction digits.",
+ (int)len, value, fraction_digits);
+ }
+ if (fraction) {
+ size = len + (fraction_digits - (len - 1 - fraction));
+ } else {
+ size = len + fraction_digits + 1;
+ }
+
+ if (len + trailing_zeros < value_len) {
+ /* consume trailing whitespaces to check that there is nothing after it */
+ uint64_t u;
+
+ for (u = len + trailing_zeros; u < value_len && isspace(value[u]); ++u) {}
+ if (u != value_len) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid %" PRIu64 ". character of decimal64 value \"%.*s\".", u + 1, (int)value_len, value);
+ }
+ }
+
+ /* prepare value string without decimal point to easily parse using standard functions */
+ valcopy = malloc(size * sizeof *valcopy);
+ if (!valcopy) {
+ return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
+ }
+
+ valcopy[size - 1] = '\0';
+ if (fraction) {
+ memcpy(&valcopy[0], &value[0], fraction);
+ memcpy(&valcopy[fraction], &value[fraction + 1], len - 1 - (fraction));
+ /* add trailing zero characters */
+ memset(&valcopy[len - 1], '0', fraction_digits - (len - 1 - fraction));
+ } else {
+ memcpy(&valcopy[0], &value[0], len);
+ /* add trailing zero characters */
+ 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, size - 1, &d, err);
+ if (!ret_val && ret) {
+ *ret = d;
+ }
+ free(valcopy);
+
+ return ret_val;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len, struct ly_err_item **err)
+{
+ int rc, match_opts;
+ LY_ARRAY_COUNT_TYPE u;
+ pcre2_match_data *match_data = NULL;
+
+ LY_CHECK_ARG_RET(NULL, str, err, LY_EINVAL);
+
+ *err = NULL;
+
+ LY_ARRAY_FOR(patterns, u) {
+ /* match_data needs to be allocated each time because of possible multi-threaded evaluation */
+ match_data = pcre2_match_data_create_from_pattern(patterns[u]->code, NULL);
+ if (!match_data) {
+ return ly_err_new(err, LY_EMEM, 0, NULL, NULL, LY_EMEM_MSG);
+ }
+
+ match_opts = PCRE2_ANCHORED;
+#ifdef PCRE2_ENDANCHORED
+ /* PCRE2_ENDANCHORED was added in PCRE2 version 10.30 */
+ match_opts |= PCRE2_ENDANCHORED;
+#endif
+ rc = pcre2_match(patterns[u]->code, (PCRE2_SPTR)str, str_len, 0, match_opts, match_data, NULL);
+ pcre2_match_data_free(match_data);
+
+ if ((rc != PCRE2_ERROR_NOMATCH) && (rc < 0)) {
+ PCRE2_UCHAR pcre2_errmsg[LY_PCRE2_MSG_LIMIT] = {0};
+
+ pcre2_get_error_message(rc, pcre2_errmsg, LY_PCRE2_MSG_LIMIT);
+
+ return ly_err_new(err, LY_ESYS, 0, NULL, NULL, "%s", (const char *)pcre2_errmsg);
+ } else if (((rc == PCRE2_ERROR_NOMATCH) && !patterns[u]->inverted) ||
+ ((rc != PCRE2_ERROR_NOMATCH) && patterns[u]->inverted)) {
+ char *eapptag = patterns[u]->eapptag ? strdup(patterns[u]->eapptag) : NULL;
+
+ if (patterns[u]->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", patterns[u]->emsg);
+ } else {
+ const char *inverted = patterns[u]->inverted ? "inverted " : "";
+
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
+ LY_ERRMSG_NOPATTERN, (int)str_len, str, inverted, patterns[u]->expr);
+ }
+ }
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value, const char *strval,
+ size_t strval_len, struct ly_err_item **err)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool is_length; /* length or range */
+
+ *err = NULL;
+ is_length = (basetype == LY_TYPE_BINARY || basetype == LY_TYPE_STRING) ? 1 : 0;
+
+ LY_ARRAY_FOR(range->parts, u) {
+ if (basetype < LY_TYPE_DEC64) {
+ /* unsigned */
+ if ((uint64_t)value < range->parts[u].min_u64) {
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
+ 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 */
+ return LY_SUCCESS;
+ } else if (u == LY_ARRAY_COUNT(range->parts) - 1) {
+ /* we have the last range part, so the value is out of bounds */
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag,
+ is_length ? LY_ERRMSG_NOLENGTH : LY_ERRMSG_NORANGE, (int)strval_len, strval);
+ }
+ }
+ } else {
+ /* signed */
+ if (value < range->parts[u].min_64) {
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ 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 */
+ return LY_SUCCESS;
+ } else if (u == LY_ARRAY_COUNT(range->parts) - 1) {
+ /* we have the last range part, so the value is out of bounds */
+ char *eapptag = range->eapptag ? strdup(range->eapptag) : NULL;
+
+ if (range->emsg) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, "%s", range->emsg);
+ } else {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, eapptag, LY_ERRMSG_NORANGE, (int)strval_len, strval);
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_prefix_data_new(const struct ly_ctx *ctx, const void *value, size_t value_len, LY_VALUE_FORMAT format,
+ const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p)
+{
+ LY_CHECK_ARG_RET(ctx, value, format_p, prefix_data_p, LY_EINVAL);
+
+ *prefix_data_p = NULL;
+ return ly_store_prefix_data(ctx, value, value_len, format, prefix_data, format_p, (void **)prefix_data_p);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_prefix_data_dup(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *orig, void **dup)
+{
+ LY_CHECK_ARG_RET(NULL, dup, LY_EINVAL);
+
+ *dup = NULL;
+ if (!orig) {
+ return LY_SUCCESS;
+ }
+
+ return ly_dup_prefix_data(ctx, format, orig, (void **)dup);
+}
+
+LIBYANG_API_DEF void
+lyplg_type_prefix_data_free(LY_VALUE_FORMAT format, void *prefix_data)
+{
+ ly_free_prefix_data(format, prefix_data);
+}
+
+static int
+type_get_hints_base(uint32_t hints)
+{
+ /* set allowed base */
+ switch (hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM)) {
+ case LYD_VALHINT_DECNUM:
+ return LY_BASE_DEC;
+ case LYD_VALHINT_OCTNUM:
+ return LY_BASE_OCT;
+ case LYD_VALHINT_HEXNUM:
+ return LY_BASE_HEX;
+ default:
+ /* generic base - decimal by default, hexa if prexed by 0x/0X and octal otherwise if prefixed by 0 */
+ return 0;
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type, int *base,
+ struct ly_err_item **err)
+{
+ LY_CHECK_ARG_RET(NULL, value || !value_len, err, LY_EINVAL);
+
+ *err = NULL;
+ if (!value) {
+ value = "";
+ }
+
+ switch (type) {
+ case LY_TYPE_UINT8:
+ case LY_TYPE_UINT16:
+ case LY_TYPE_UINT32:
+ case LY_TYPE_INT8:
+ case LY_TYPE_INT16:
+ case LY_TYPE_INT32:
+ LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
+
+ if (!(hints & (LYD_VALHINT_DECNUM | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM))) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-number-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ *base = type_get_hints_base(hints);
+ break;
+ case LY_TYPE_UINT64:
+ case LY_TYPE_INT64:
+ LY_CHECK_ARG_RET(NULL, base, LY_EINVAL);
+
+ if (!(hints & LYD_VALHINT_NUM64)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-num64-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ *base = type_get_hints_base(hints);
+ break;
+ case LY_TYPE_STRING:
+ case LY_TYPE_DEC64:
+ case LY_TYPE_ENUM:
+ case LY_TYPE_BITS:
+ case LY_TYPE_BINARY:
+ case LY_TYPE_IDENT:
+ case LY_TYPE_INST:
+ if (!(hints & LYD_VALHINT_STRING)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-string-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ break;
+ case LY_TYPE_BOOL:
+ if (!(hints & LYD_VALHINT_BOOLEAN)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-boolean-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ break;
+ case LY_TYPE_EMPTY:
+ if (!(hints & LYD_VALHINT_EMPTY)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid non-empty-encoded %s value \"%.*s\".",
+ lys_datatype2str(type), (int)value_len, value);
+ }
+ break;
+ case LY_TYPE_UNKNOWN:
+ case LY_TYPE_LEAFREF:
+ case LY_TYPE_UNION:
+ LOGINT_RET(NULL);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_check_status(const struct lysc_node *ctx_node, uint16_t val_flags, LY_VALUE_FORMAT format, void *prefix_data,
+ const char *val_name, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ const struct lys_module *mod2;
+ uint16_t flg1, flg2;
+
+ if (format != LY_VALUE_SCHEMA) {
+ /* nothing/unable to check */
+ return LY_SUCCESS;
+ }
+
+ mod2 = ((struct lysp_module *)prefix_data)->mod;
+
+ if (mod2 == ctx_node->module) {
+ /* use flags of the context node since the definition is local */
+ flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ } else {
+ /* definition is foreign (deviation, refine), always current */
+ flg1 = LYS_STATUS_CURR;
+ }
+ flg2 = (val_flags & LYS_STATUS_MASK) ? (val_flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+
+ if ((flg1 < flg2) && (ctx_node->module == mod2)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL,
+ "A %s definition \"%s\" is not allowed to reference %s value \"%s\".",
+ flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name,
+ flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", val_name);
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_lypath_check_status(const struct lysc_node *ctx_node, const struct ly_path *path, LY_VALUE_FORMAT format,
+ void *prefix_data, struct ly_err_item **err)
+{
+ LY_ERR ret;
+ LY_ARRAY_COUNT_TYPE u;
+ const struct lys_module *val_mod;
+ const struct lysc_node *node;
+ uint16_t flg1, flg2;
+
+ if (format != LY_VALUE_SCHEMA) {
+ /* nothing to check */
+ return LY_SUCCESS;
+ }
+
+ val_mod = ((struct lysp_module *)prefix_data)->mod;
+ if (val_mod == ctx_node->module) {
+ /* use flags of the context node since the definition is local */
+ flg1 = (ctx_node->flags & LYS_STATUS_MASK) ? (ctx_node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ } else {
+ /* definition is foreign (deviation, refine), always current */
+ flg1 = LYS_STATUS_CURR;
+ }
+
+ LY_ARRAY_FOR(path, u) {
+ node = path[u].node;
+
+ flg2 = (node->flags & LYS_STATUS_MASK) ? (node->flags & LYS_STATUS_MASK) : LYS_STATUS_CURR;
+ if ((flg1 < flg2) && (val_mod == node->module)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_REFERENCE, NULL, NULL,
+ "A %s definition \"%s\" is not allowed to reference %s value \"%s\".",
+ flg1 == LYS_STATUS_CURR ? "current" : "deprecated", ctx_node->name,
+ flg2 == LYS_STATUS_OBSLT ? "obsolete" : "deprecated", node->name);
+ return ret;
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_len, uint32_t options,
+ LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node, struct lys_glob_unres *unres,
+ struct ly_path **path, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ uint32_t prefix_opt = 0;
+
+ LY_CHECK_ARG_RET(ctx, ctx, value, ctx_node, path, err, LY_EINVAL);
+
+ *path = NULL;
+ *err = NULL;
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ prefix_opt = LY_PATH_PREFIX_MANDATORY;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_JSON:
+ case LY_VALUE_STR_NS:
+ prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
+ break;
+ }
+
+ /* parse the value */
+ ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
+ if (ret) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, value);
+ goto cleanup;
+ }
+
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ /* implement all prefixes */
+ LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup);
+ }
+
+ /* resolve it on schema tree */
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node->flags & LYS_IS_OUTPUT) ?
+ LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_SINGLE, 1, format, prefix_data, path);
+ if (ret) {
+ ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, value);
+ goto cleanup;
+ }
+
+cleanup:
+ lyxp_expr_free(ctx, exp);
+ if (ret) {
+ ly_path_free(ctx, *path);
+ *path = NULL;
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_lypath_free(const struct ly_ctx *ctx, struct ly_path *path)
+{
+ ly_path_free(ctx, path);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_make_implemented(struct lys_module *mod, const char **features, struct lys_glob_unres *unres)
+{
+ if (mod->implemented) {
+ return LY_SUCCESS;
+ }
+
+ LY_CHECK_RET(lys_implement(mod, features, unres));
+ LY_CHECK_RET(lys_compile(mod, &unres->ds_unres));
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_identity_isderived(const struct lysc_ident *base, const struct lysc_ident *der)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ assert(base->module->ctx == der->module->ctx);
+
+ LY_ARRAY_FOR(base->derived, u) {
+ if (der == base->derived[u]) {
+ return LY_SUCCESS;
+ }
+ if (!lyplg_type_identity_isderived(base->derived[u], der)) {
+ return LY_SUCCESS;
+ }
+ }
+ return LY_ENOTFOUND;
+}
+
+/**
+ * @brief Try to generate a path to the leafref target with its value to enable the use of hash-based search.
+ *
+ * @param[in] path Leafref path.
+ * @param[in] ctx_node Leafref context node.
+ * @param[in] format Format of @p path.
+ * @param[in] prefix_data Prefix data of @p path.
+ * @param[in] target_val Leafref target value.
+ * @param[out] target_path Generated path with the target value.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if no matching target exists.
+ * @return LY_ERR on error.
+ */
+static LY_ERR
+lyplg_type_resolve_leafref_get_target_path(const struct lyxp_expr *path, const struct lysc_node *ctx_node,
+ LY_VALUE_FORMAT format, void *prefix_data, const char *target_val, struct lyxp_expr **target_path)
+{
+ LY_ERR rc = LY_SUCCESS;
+ uint8_t oper;
+ struct ly_path *p = NULL;
+ char *str_path = NULL, quot;
+ int len;
+ ly_bool list_key = 0;
+
+ *target_path = NULL;
+
+ /* compile, has already been so it must succeed */
+ oper = (ctx_node->flags & LYS_IS_OUTPUT) ? LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT;
+ if (ly_path_compile_leafref(ctx_node->module->ctx, ctx_node, NULL, path, oper, LY_PATH_TARGET_MANY, format,
+ prefix_data, &p)) {
+ /* the target was found before but is disabled so it was removed */
+ return LY_ENOT;
+ }
+
+ /* check whether we can search for a list instance with a specific key value */
+ if (lysc_is_key(p[LY_ARRAY_COUNT(p) - 1].node)) {
+ if ((LY_ARRAY_COUNT(p) >= 2) && (p[LY_ARRAY_COUNT(p) - 2].node->nodetype == LYS_LIST)) {
+ if ((path->tokens[path->used - 1] == LYXP_TOKEN_NAMETEST) &&
+ (path->tokens[path->used - 2] == LYXP_TOKEN_OPER_PATH) &&
+ (path->tokens[path->used - 3] == LYXP_TOKEN_NAMETEST)) {
+ list_key = 1;
+ } /* else again, should be possible but does not make sense */
+ } /* else allowed despite not making sense */
+ }
+
+ if (list_key) {
+ /* get the length of the orig expression without the last "/" and the key node */
+ len = path->tok_pos[path->used - 3] + path->tok_len[path->used - 3];
+
+ /* generate the string path evaluated using hashes */
+ quot = strchr(target_val, '\'') ? '\"' : '\'';
+ if (asprintf(&str_path, "%.*s[%s=%c%s%c]/%s", len, path->expr, path->expr + path->tok_pos[path->used - 1],
+ quot, target_val, quot, path->expr + path->tok_pos[path->used - 1]) == -1) {
+ LOGMEM(ctx_node->module->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+
+ } else {
+ /* leaf will not be found using hashes, but generate the path just to unify it */
+ assert(p[LY_ARRAY_COUNT(p) - 1].node->nodetype & LYD_NODE_TERM);
+
+ /* generate the string path evaluated using hashes */
+ quot = strchr(target_val, '\'') ? '\"' : '\'';
+ if (asprintf(&str_path, "%s[.=%c%s%c]", path->expr, quot, target_val, quot) == -1) {
+ LOGMEM(ctx_node->module->ctx);
+ rc = LY_EMEM;
+ goto cleanup;
+ }
+ }
+
+ /* parse into an expression */
+ LY_CHECK_GOTO(lyxp_expr_parse(ctx_node->module->ctx, str_path, 0, 1, target_path), cleanup);
+
+cleanup:
+ ly_path_free(ctx_node->module->ctx, p);
+ free(str_path);
+ return rc;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node, struct lyd_value *value,
+ const struct lyd_node *tree, struct lyd_node **target, char **errmsg)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lyxp_expr *target_path = NULL;
+ struct lyxp_set set = {0};
+ const char *val_str, *xp_err_msg;
+ uint32_t i;
+ int r;
+
+ LY_CHECK_ARG_RET(NULL, lref, node, value, errmsg, LY_EINVAL);
+
+ if (target) {
+ *target = NULL;
+ }
+
+ /* get the canonical value */
+ val_str = lyd_value_get_canonical(LYD_CTX(node), value);
+
+ if (!strchr(val_str, '\"') || !strchr(val_str, '\'')) {
+ /* get the path with the value */
+ r = lyplg_type_resolve_leafref_get_target_path(lref->path, node->schema, LY_VALUE_SCHEMA_RESOLVED, lref->prefixes,
+ val_str, &target_path);
+ if (r == LY_ENOT) {
+ goto cleanup;
+ } else if (r) {
+ rc = r;
+ goto cleanup;
+ }
+ } /* else value with both ' and ", XPath does not support that */
+
+ /* find the target data instance(s) */
+ rc = lyxp_eval(LYD_CTX(node), target_path ? target_path : lref->path, node->schema->module,
+ LY_VALUE_SCHEMA_RESOLVED, lref->prefixes, node, node, tree, NULL, &set, LYXP_IGNORE_WHEN);
+ if (rc) {
+ if (ly_errcode(LYD_CTX(node)) == rc) {
+ xp_err_msg = ly_errmsg(LYD_CTX(node));
+ } else {
+ xp_err_msg = NULL;
+ }
+
+ if (xp_err_msg) {
+ r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error (%s).", val_str, xp_err_msg);
+ } else {
+ r = asprintf(errmsg, "Invalid leafref value \"%s\" - XPath evaluation error.", val_str);
+ }
+ if (r == -1) {
+ *errmsg = NULL;
+ rc = LY_EMEM;
+ }
+ goto cleanup;
+ }
+
+ /* check the result */
+ if (target_path) {
+ /* no or exact match(es) */
+ i = 0;
+ } else {
+ /* check whether any matches */
+ for (i = 0; i < set.used; ++i) {
+ if (set.val.nodes[i].type != LYXP_NODE_ELEM) {
+ continue;
+ }
+
+ if (!lref->plugin->compare(&((struct lyd_node_term *)set.val.nodes[i].node)->value, value)) {
+ break;
+ }
+ }
+ }
+
+ if (i == set.used) {
+ /* no match found */
+ rc = LY_ENOTFOUND;
+ if (asprintf(errmsg, LY_ERRMSG_NOLREF_VAL, val_str, lref->path->expr) == -1) {
+ *errmsg = NULL;
+ rc = LY_EMEM;
+ }
+ goto cleanup;
+ }
+ if (target) {
+ *target = set.val.nodes[i].node;
+ }
+
+cleanup:
+ lyxp_expr_free(LYD_CTX(node), target_path);
+ lyxp_set_free_content(&set);
+ return rc;
+}
diff --git a/src/plugins_types.h b/src/plugins_types.h
new file mode 100644
index 0000000..3ec1d3b
--- /dev/null
+++ b/src/plugins_types.h
@@ -0,0 +1,1214 @@
+/**
+ * @file plugins_types.h
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief API for (user) types plugins
+ *
+ * Copyright (c) 2019 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
+ */
+
+#ifndef LY_PLUGINS_TYPES_H_
+#define LY_PLUGINS_TYPES_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "config.h"
+#include "log.h"
+#include "plugins.h"
+#include "tree.h"
+
+#include "tree_edit.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct ly_ctx;
+struct ly_path;
+struct lyd_node;
+struct lyd_value;
+struct lyd_value_xpath10;
+struct lys_module;
+struct lys_glob_unres;
+struct lysc_ident;
+struct lysc_node;
+struct lysc_pattern;
+struct lysc_range;
+struct lysc_type;
+struct lysc_type_bits;
+struct lysc_type_leafref;
+
+/**
+ * @page howtoPluginsTypes Type Plugins
+ *
+ * Note that the part of the libyang API here is available only by including a separated `<libyang/plugins_types.h>` header
+ * file. Also note that the type plugins API is versioned separately from libyang itself, so backward incompatible changes
+ * can come even without changing libyang major version.
+ *
+ * YANG allows to define new data types via *typedef* statements or even in leaf's/leaf-list's *type* statements.
+ * Such types are derived (directly or indirectly) from a set of [YANG built-in types](https://tools.ietf.org/html/rfc7950#section-4.2.4).
+ * libyang implements all handling of the data values of the YANG types via the Type Plugins API. Internally, there is
+ * implementation of the built-in types and others can be added as an external plugin (see @ref howtoPlugins).
+ *
+ * Type plugin is supposed to
+ * - store (and canonize) data value,
+ * - validate it according to the type's restrictions,
+ * - compare two values (::lyd_value) of the same type,
+ * - duplicate value (::lyd_value),
+ * - print it and
+ * - free the specific data inserted into ::lyd_value.
+ *
+ * These tasks are implemented as callbacks provided to libyang via ::lyplg_type_record structures defined as array using
+ * ::LYPLG_TYPES macro.
+ *
+ * All the callbacks are supposed to do not log directly via libyang logger. Instead, they return ::LY_ERR value and
+ * ::ly_err_item error structure(s) describing the detected error(s) (helper functions ::ly_err_new() and ::ly_err_free()
+ * are available).
+ *
+ * The main functionality is provided via ::lyplg_type_store_clb callback responsible for canonizing and storing
+ * provided string representation of the value in specified format (XML and JSON supported). Valid value is stored in
+ * ::lyd_value structure - its union allows to store data as one of the predefined type or in a custom form behind
+ * the void *ptr member of ::lyd_value structure. The callback is also responsible for storing canonized string
+ * representation of the value as ::lyd_value._canonical. If the type does not define canonical representation, the original
+ * representation is stored. In case there are any differences between the representation in specific input types, the plugin
+ * is supposed to store the value in JSON representation - typically, the difference is in prefix representation and JSON
+ * format uses directly the module names as prefixes.
+ *
+ * Usually, all the validation according to the type's restrictions is done in the store callback. However, in case the type
+ * requires some validation referencing other entities in the data tree, the optional validation callback
+ * ::lyplg_type_validate_clb can be implemented.
+ *
+ * The stored values can be compared in a specific way by providing ::lyplg_type_compare_clb. In case the best way to compare
+ * the values is to compare their canonical string representations, the ::lyplg_type_compare_simple() function can be used.
+ *
+ * Data duplication is done with ::lyplg_type_dup_clb callbacks. Note that the callback is responsible even for duplicating
+ * the ::lyd_value._canonical, so the callback must be always present (the canonical value is always present). If there is
+ * nothing else to duplicate, the plugin can use the generic ::lyplg_type_dup_simple().
+ *
+ * The stored value can be printed into the required format via ::lyplg_type_print_clb implementation. Simple printing
+ * canonical representation of the value is implemented by ::lyplg_type_print_simple().
+ *
+ * And finally freeing any data stored in the ::lyd_value by the plugin is done by implementation of ::lyplg_type_free_clb.
+ * Freeing only the canonical string is implemented by ::lyplg_type_free_simple().
+ *
+ * The plugin information contains also the plugin identifier (::lyplg_type.id). This string can serve to identify the
+ * specific plugin responsible to storing data value. In case the user can recognize the id string, it can access the
+ * plugin specific data with the appropriate knowledge of its structure.
+ *
+ * Besides the mentioned `_simple` functions, libyang provides, as part of the type plugins API, all the callbacks
+ * implementing the built-in types in the internal plugins:
+ *
+ * - [simple callbacks](@ref pluginsTypesSimple) handling only the canonical strings in the value,
+ * - [binary built-in type](@ref pluginsTypesBinary)
+ * - [bits built-in type](@ref pluginsTypesBits)
+ * - [boolean built-in type](@ref pluginsTypesBoolean)
+ * - [decimal64 built-in type](@ref pluginsTypesDecimal64)
+ * - [empty built-in type](@ref pluginsTypesEmpty)
+ * - [enumeration built-in type](@ref pluginsTypesEnumeration)
+ * - [identityref built-in type](@ref pluginsTypesIdentityref)
+ * - [instance-identifier built-in type](@ref pluginsTypesInstanceid)
+ * - [integer built-in types](@ref pluginsTypesInteger)
+ * - [leafref built-in type](@ref pluginsTypesLeafref)
+ * - [string built-in type](@ref pluginsTypesString)
+ * - [union built-in type](@ref pluginsTypesUnion)
+ *
+ * And one derived type:
+ *
+ * - [xpath1.0 `ietf-yang-types` type](@ref pluginsTypesXpath10)
+ *
+ * In addition to these callbacks, the API also provides several functions which can help to implement your own plugin for the
+ * derived YANG types:
+ *
+ * - ::ly_err_new()
+ * - ::ly_err_free()
+ *
+ * - ::lyplg_type_lypath_new()
+ * - ::lyplg_type_lypath_free()
+ *
+ * - ::lyplg_type_prefix_data_new()
+ * - ::lyplg_type_prefix_data_dup()
+ * - ::lyplg_type_prefix_data_free()
+ * - ::lyplg_type_get_prefix()
+ *
+ * - ::lyplg_type_check_hints()
+ * - ::lyplg_type_check_status()
+ * - ::lyplg_type_lypath_check_status()
+ * - ::lyplg_type_identity_isderived()
+ * - ::lyplg_type_identity_module()
+ * - ::lyplg_type_make_implemented()
+ * - ::lyplg_type_parse_dec64()
+ * - ::lyplg_type_parse_int()
+ * - ::lyplg_type_parse_uint()
+ * - ::lyplg_type_resolve_leafref()
+ */
+
+/**
+ * @defgroup pluginsTypes Plugins: Types
+ * @{
+ *
+ * Structures and functions to for libyang plugins implementing specific YANG types defined in YANG modules. For more
+ * information, see @ref howtoPluginsTypes.
+ *
+ * This part of libyang API is available by including `<libyang/plugins_types.h>` header file.
+ */
+
+/**
+ * @brief Type API version
+ */
+#define LYPLG_TYPE_API_VERSION 1
+
+/**
+ * @brief Macro to define plugin information in external plugins
+ *
+ * Use as follows:
+ * LYPLG_TYPES = {{<filled information of ::lyplg_type_record>}, ..., {0}};
+ */
+#define LYPLG_TYPES \
+ uint32_t plugins_types_apiver__ = LYPLG_TYPE_API_VERSION; \
+ const struct lyplg_type_record plugins_types__[]
+
+/**
+ * @brief Check whether specific type value needs to be allocated dynamically.
+ *
+ * @param[in] type_val Pointer to specific type value storage.
+ */
+#define LYPLG_TYPE_VAL_IS_DYN(type_val) \
+ (sizeof *(type_val) > LYD_VALUE_FIXED_MEM_SIZE)
+
+/**
+ * @brief Prepare value memory for storing a specific type value, may be allocated dynamically.
+ *
+ * Must be called for values larger than 8 bytes.
+ * To be used in ::lyplg_type_store_clb.
+ *
+ * @param[in] storage Pointer to the value storage to use (struct ::lyd_value *).
+ * @param[in,out] type_val Pointer to specific type value structure.
+ */
+#define LYPLG_TYPE_VAL_INLINE_PREPARE(storage, type_val) \
+ (LYPLG_TYPE_VAL_IS_DYN(type_val) \
+ ? ((type_val) = ((storage)->dyn_mem = calloc(1, sizeof *(type_val)))) \
+ : ((type_val) = memset((storage)->fixed_mem, 0, sizeof *(type_val))))
+
+/**
+ * @brief Destroy a prepared value.
+ *
+ * Must be called for values prepared with ::LYPLG_TYPE_VAL_INLINE_PREPARE.
+ *
+ * @param[in] type_val Pointer to specific type value structure.
+ */
+#define LYPLG_TYPE_VAL_INLINE_DESTROY(type_val) \
+ do { if (LYPLG_TYPE_VAL_IS_DYN(type_val)) free(type_val); } while(0)
+
+/**
+ * @brief Create and fill error structure.
+ *
+ * Helper function for various plugin functions to generate error information structure.
+ *
+ * @param[in, out] err Pointer to store a new error structure filled according to the input parameters. If the storage
+ * already contains error information, the new record is appended into the errors list.
+ * @param[in] ecode Code of the error to fill. In case LY_SUCCESS value, nothing is done and LY_SUCCESS is returned.
+ * @param[in] vecode Validity error code in case of LY_EVALID error code.
+ * @param[in] path Path to the node causing the error.
+ * @param[in] apptag Error-app-tag value.
+ * @param[in] err_format Format string (same like at printf) or string literal.
+ * If you want to print just an unknown string, use "%s" for the @p err_format, otherwise undefined behavior may occur
+ * because the unknown string may contain the % character, which is interpreted as conversion specifier.
+ * @return The given @p ecode value if the @p err is successfully created. The structure can be freed using ::ly_err_free()
+ * or passed back from callback into libyang.
+ * @return LY_EMEM If there is not enough memory for allocating error record, the @p err is not touched in that case.
+ * @return LY_SUCCESS if @p ecode is LY_SUCCESS, the @p err is not touched in this case.
+ */
+LIBYANG_API_DECL LY_ERR ly_err_new(struct ly_err_item **err, LY_ERR ecode, LY_VECODE vecode, char *path, char *apptag,
+ const char *err_format, ...) _FORMAT_PRINTF(6, 7);
+
+/**
+ * @brief Destructor for the error records created with ::ly_err_new().
+ *
+ * Compatible with the free(), so usable as a generic callback.
+ *
+ * @param[in] ptr Error record (::ly_err_item, the void pointer is here only for compatibility with a generic free()
+ * function) to free. With the record, also all the records (if any) connected after this one are freed.
+ */
+LIBYANG_API_DECL void ly_err_free(void *ptr);
+
+/**
+ * @brief Check that the type is suitable for the parser's hints (if any) in the specified format
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types provided by parsers
+ * to ::lyplg_type_store_clb.
+ * @param[in] value Lexical representation of the value to be stored.
+ * @param[in] value_len Length (number of bytes) of the given \p value.
+ * @param[in] type Expected base type of the @p value by the caller.
+ * @param[out] base Pointer to store the numeric base for parsing numeric values using strtol()/strtoll() function.
+ * Returned (and required) only for numeric @p type values.
+ * @param[out] err Pointer to store error information in case of failure.
+ * @return LY_ERR value
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_check_hints(uint32_t hints, const char *value, size_t value_len, LY_DATA_TYPE type,
+ int *base, struct ly_err_item **err);
+
+/**
+ * @brief Check that the value of a type is allowed based on its status.
+ *
+ * @param[in] ctx_node Context node (which references the value).
+ * @param[in] val_flags Flags fo the value.
+ * @param[in] format Format of the value.
+ * @param[in] prefix_data Prefix data of the value.
+ * @param[in] val_name Name of the value, only for logging.
+ * @param[out] err Pointer to store error information in case of failure.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_check_status(const struct lysc_node *ctx_node, uint16_t val_flags, LY_VALUE_FORMAT format,
+ void *prefix_data, const char *val_name, struct ly_err_item **err);
+
+/**
+ * @brief Check that the lypath instance-identifier value is allowed based on the status of the nodes.
+ *
+ * @param[in] ctx_node Context node (which references the value).
+ * @param[in] path Path of the instance-identifier.
+ * @param[in] format Format of the value.
+ * @param[in] prefix_data Prefix data of the value.
+ * @param[out] err Pointer to store error information in case of failure.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_lypath_check_status(const struct lysc_node *ctx_node, const struct ly_path *path,
+ LY_VALUE_FORMAT format, void *prefix_data, struct ly_err_item **err);
+
+/**
+ * @brief Get the corresponding module for the identity value.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] ctx_node Schema node where the value is instantiated to determine the module in case of unprefixed value
+ * in specific @p format.
+ * @param[in] prefix Prefix to resolve - identified beginning of a prefix in ::lyplg_type_store_clb's value parameter.
+ * If NULL, an unprefixed identity is resolved.
+ * @param[in] prefix_len Length of @p prefix.
+ * @param[in] format Format of the prefix (::lyplg_type_store_clb's format parameter).
+ * @param[in] prefix_data Format-specific data (::lyplg_type_store_clb's prefix_data parameter).
+ * @return Resolved prefix module,
+ * @return NULL otherwise.
+ */
+LIBYANG_API_DECL const struct lys_module *lyplg_type_identity_module(const struct ly_ctx *ctx,
+ const struct lysc_node *ctx_node, const char *prefix, size_t prefix_len, LY_VALUE_FORMAT format,
+ const void *prefix_data);
+
+/**
+ * @brief Implement a module (just like ::lys_set_implemented()), but keep maintaining unresolved items.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] mod Module to implement.
+ * @param[in] features Array of features to enable.
+ * @param[in,out] unres Global unres to add to.
+ * @return LY_ERECOMPILE if the context need to be recompiled, should be returned.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL ly_bool lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position);
+
+/**
+ * @brief Print xpath1.0 token in the specific format.
+ *
+ * @param[in] token Token to transform.
+ * @param[in] tok_len Lenghth of @p token.
+ * @param[in] is_nametest Whether the token is a nametest, it then always requires a prefix in XML @p get_format.
+ * @param[in,out] context_mod Current context module, may be updated.
+ * @param[in] resolve_ctx Context to use for resolving prefixes.
+ * @param[in] resolve_format Format of the resolved prefixes.
+ * @param[in] resolve_prefix_data Resolved prefixes prefix data.
+ * @param[in] get_format Format of the output prefixes.
+ * @param[in] get_prefix_data Format-specific prefix data for the output.
+ * @param[out] token_p Printed token.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DEF LY_ERR lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_nametest,
+ const struct lys_module **context_mod, const struct ly_ctx *resolve_ctx, LY_VALUE_FORMAT resolve_format,
+ const void *resolve_prefix_data, LY_VALUE_FORMAT get_format, void *get_prefix_data, char **token_p,
+ struct ly_err_item **err);
+
+/**
+ * @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.
+ *
+ * @param[in] mod Module whose prefix to get - the module somehow connected with the value to print.
+ * @param[in] format Format of the prefix (::lyplg_type_print_clb's format parameter).
+ * @param[in] prefix_data Format-specific data (::lyplg_type_print_clb's prefix_data parameter).
+ * @return Module prefix to print.
+ * @return NULL on error.
+ */
+LIBYANG_API_DECL const char *lyplg_type_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @brief Store used prefixes in a string into an internal libyang structure used in ::lyd_value.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * If @p prefix_data_p are non-NULL, they are treated as valid according to the @p format_p and new possible
+ * prefixes are simply added. This way it is possible to store prefix data for several strings together.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] value Value to be parsed.
+ * @param[in] value_len Length of @p value.
+ * @param[in] format Format of the prefixes in the value.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in,out] format_p Resulting format of the prefixes.
+ * @param[in,out] prefix_data_p Resulting prefix data for the value in format @p format_p.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_prefix_data_new(const struct ly_ctx *ctx, const void *value, size_t value_len,
+ LY_VALUE_FORMAT format, const void *prefix_data, LY_VALUE_FORMAT *format_p, void **prefix_data_p);
+/**
+ * @brief Duplicate prefix data.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] format Format of the prefixes in the value.
+ * @param[in] orig Prefix data to duplicate.
+ * @param[out] dup Duplicated prefix data.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_prefix_data_dup(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void *orig,
+ void **dup);
+
+/**
+ * @brief Free internal prefix data.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] format Format of the prefixes.
+ * @param[in] prefix_data Format-specific data to free.
+ */
+LIBYANG_API_DECL void lyplg_type_prefix_data_free(LY_VALUE_FORMAT format, void *prefix_data);
+
+/**
+ * @brief Helper function to create internal schema path representation for instance-identifier value representation.
+ *
+ * Use only in implementations of ::lyplg_type_store_clb which provide all the necessary parameters for this function.
+ *
+ * @param[in] ctx libyang Context
+ * @param[in] value Lexical representation of the 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.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in] ctx_node The @p value schema context node.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] path Pointer to store the created structure representing the schema path from the @p value.
+ * @param[out] err Pointer to store the error information provided in case of failure.
+ * @return LY_SUCCESS on success,
+ * @return LY_ERECOMPILE if the context need to be recompiled, should be returned.
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, const struct lysc_node *ctx_node,
+ struct lys_glob_unres *unres, struct ly_path **path, struct ly_err_item **err);
+
+/**
+ * @brief Free ly_path structure used by instanceid value representation.
+ *
+ * The ly_path representation can be created by ::lyplg_type_lypath_new().
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] path The structure ([sized array](@ref sizedarrays)) to free.
+ */
+LIBYANG_API_DECL void lyplg_type_lypath_free(const struct ly_ctx *ctx, struct ly_path *path);
+
+/**
+ * @brief Print xpath1.0 value in the specific format.
+ *
+ * @param[in] xp_val xpath1.0 value structure.
+ * @param[in] format Format to print in.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[out] str_value Printed value.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_print_xpath10_value(const struct lyd_value_xpath10 *xp_val, LY_VALUE_FORMAT format,
+ void *prefix_data, char **str_value, struct ly_err_item **err);
+
+/**
+ * @defgroup plugintypestoreopts Plugins: Type store callback options.
+ *
+ * Options applicable to ::lyplg_type_store_clb().
+ *
+ * @{
+ */
+#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
+ 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 */
+
+/**
+ * @brief Callback to store the given @p value according to the given @p type.
+ *
+ * Value must always be correctly stored meaning all the other type callbacks (such as print or compare)
+ * must function as expected. However, ::lyd_value._canonical can be left NULL and will be generated
+ * 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 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 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.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in] hints Bitmap of [value hints](@ref lydvalhints) of all the allowed value types.
+ * @param[in] ctx_node Schema context node of @p value, may be NULL for metadata.
+ * @param[out] storage Storage for the value in the type's specific encoding. Except for _canonical_, all the members
+ * should be filled by the plugin (if it fills them at all).
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
+ * error message is prepared instead. The error structure can be created by ::ly_err_new().
+ * @return LY_SUCCESS on success,
+ * @return LY_EINCOMPLETE in case the ::lyplg_type_validate_clb should be called to finish value validation in data,
+ * @return LY_ERR value on error, @p storage must not have any pointers to dynamic memory.
+ */
+LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_store_clb)(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 Callback to validate the stored value in data.
+ *
+ * This callback is optional for types that can only be validated in a data tree. It must be called and succeed
+ * in case the ::lyplg_type_store_clb callback returned ::LY_EINCOMPLETE for the value to be valid. However, this
+ * callback can be called even in other cases (such as separate/repeated validation).
+ *
+ * @param[in] ctx libyang context
+ * @param[in] type Original type of the value (not necessarily the stored one) being validated.
+ * @param[in] ctx_node The value data context node for validation.
+ * @param[in] tree External data tree (e.g. when validating RPC/Notification) with possibly referenced data.
+ * @param[in,out] storage Storage of the value successfully filled by ::lyplg_type_store_clb. May be modified.
+ * @param[out] err Optionally provided error information in case of failure. If not provided to the caller, a generic
+ * error message is prepared instead. The error structure can be created by ::ly_err_new().
+ * @return LY_SUCCESS on success,
+ * @return LY_ERR value on error.
+ */
+LIBYANG_API_DECL typedef LY_ERR (*lyplg_type_validate_clb)(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 Callback for comparing 2 values of the same type.
+ *
+ * In case the value types (::lyd_value.realtype) are different, ::LY_ENOT must always be returned.
+ * It can be assumed that the same context (dictionary) was used for storing both values.
+ *
+ * @param[in] val1 First value to compare.
+ * @param[in] val2 Second value to compare.
+ * @return LY_SUCCESS if values are same (according to the type's definition of being same).
+ * @return LY_ENOT if values differ.
+ */
+typedef LY_ERR (*lyplg_type_compare_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Unused callback for sorting values.
+ *
+ * @param[in] val1 First value to compare.
+ * @param[in] val2 Second value to compare.
+ * @return -1 if val1 < val2,
+ * @return 0 if val1 == val2,
+ * @return 1 if val1 > val2.
+ */
+typedef int (*lyplg_type_sort_clb)(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Callback for getting the value of the data stored in @p value.
+ *
+ * Canonical value (@p format of ::LY_VALUE_CANON) must always be a zero-terminated const string stored in
+ * the dictionary. The ::lyd_value._canonical member should be used for storing (caching) it.
+ *
+ * @param[in] ctx libyang context for storing the canonical value. May not be set for ::LY_VALUE_LYB format.
+ * @param[in] value Value to print.
+ * @param[in] format Format in which the data are supposed to be printed. Formats ::LY_VALUE_SCHEMA and
+ * ::LY_VALUE_SCHEMA_RESOLVED are not supported and should not be implemented.
+ * @param[in] prefix_data Format-specific data for processing prefixes. In case of using one of the built-in's print
+ * callback (or ::lyplg_type_print_simple()), the argument is just simply passed in. If you need to handle prefixes
+ * in the value on your own, there is ::lyplg_type_get_prefix() function to help.
+ * @param[out] dynamic Flag if the returned value is dynamically allocated. In such a case the caller is responsible
+ * for freeing it. Will not be set and should be ignored for @p format ::LY_VALUE_CANON.
+ * @param[out] value_len Optional returned value length in bytes. For strings it EXCLUDES the terminating zero.
+ * @return Pointer to @p value in the specified @p format. According to the returned @p dynamic flag, caller
+ * can be responsible for freeing allocated memory.
+ * @return NULL in case of error.
+ */
+typedef const void *(*lyplg_type_print_clb)(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 Callback to duplicate data in the data structure.
+ *
+ * @param[in] ctx libyang context of the @p dup. Note that the context of @p original and @p dup might not be the same.
+ * @param[in] original Original data structure to be duplicated.
+ * @param[in,out] dup Prepared data structure to be filled with the duplicated data of @p original.
+ * @return LY_SUCCESS after successful duplication.
+ * @return LY_ERR value on error.
+ */
+typedef LY_ERR (*lyplg_type_dup_clb)(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup);
+
+/**
+ * @brief Callback for freeing the user type values stored by ::lyplg_type_store_clb.
+ *
+ * Note that this callback is responsible also for freeing the canonized member in the @p value.
+ *
+ * @param[in] ctx libyang ctx to enable correct manipulation with values that are in the dictionary.
+ * @param[in,out] value Value structure to free the data stored there by the plugin's ::lyplg_type_store_clb callback
+ */
+typedef void (*lyplg_type_free_clb)(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/**
+ * @brief Hold type-specific functions for various operations with the data values.
+ *
+ * libyang includes set of plugins for all the built-in types. They are, by default, inherited to the derived types.
+ * However, if the user type plugin for the specific type is loaded, the plugin can provide it's own functions.
+ * The built-in types plugin callbacks are public, so even the user type plugins can use them to do part of their own
+ * functionality.
+ */
+struct lyplg_type {
+ const char *id; /**< Plugin identification (mainly for distinguish incompatible versions when
+ used by external tools) */
+ lyplg_type_store_clb store; /**< store and canonize the value in the type-specific way */
+ lyplg_type_validate_clb validate; /**< optional, validate the value in the type-specific way in data */
+ lyplg_type_compare_clb compare; /**< comparison callback to compare 2 values of the same type */
+ lyplg_type_sort_clb sort; /**< unused comparison callback for sorting values */
+ lyplg_type_print_clb print; /**< printer callback to get string representing the value */
+ lyplg_type_dup_clb duplicate; /**< data duplication callback */
+ lyplg_type_free_clb free; /**< optional function to free the type-spceific way stored value */
+ int32_t lyb_data_len; /**< Length of the data in [LYB format](@ref howtoDataLYB).
+ For variable-length is set to -1. */
+};
+
+struct lyplg_type_record {
+ /* plugin identification */
+ const char *module; /**< name of the module where the type is defined (top-level typedef) */
+ const char *revision; /**< optional module revision - if not specified, the plugin applies to any revision,
+ which is not an optimal approach due to a possible future revisions of the module.
+ Instead, there should be defined multiple items in the plugins list, each with the
+ different revision, but all with the same pointer to the plugin functions. The
+ only valid use case for the NULL revision is the case the module has no revision. */
+ const char *name; /**< name of the typedef */
+
+ /* runtime data */
+ struct lyplg_type plugin; /**< data to utilize plugin implementation */
+};
+
+/**
+ * @defgroup pluginsTypesSimple Plugins: Simple Types Callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Simple functions implementing @ref howtoPluginsTypes callbacks handling types that allocate no dynamic
+ * value and always generate their canonical value (::lyd_value._canonical).
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_compare_clb for a generic simple type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_simple(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for a generic simple type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_dup_clb for a generic simple type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_simple(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for a generic simple type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_simple(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesSimple */
+
+/**
+ * @defgroup pluginsTypesBinary Plugins: Binary built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used to implement binary built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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_dup_clb for the built-in binary type.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL void lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesBinary */
+
+/**
+ * @defgroup pluginsTypesBits Plugins: Bits built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement bits built-in type.
+ */
+
+/**
+ * @brief Implementation of the ::lyplg_type_store_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL 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 Implementation of the ::lyplg_type_compare_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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_dup_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of the ::lyplg_type_free_clb for the built-in bits type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesBits */
+
+/**
+ * @defgroup pluginsTypesBoolean Plugins: Boolean built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement boolean built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in boolean type.
+ */
+LIBYANG_API_DECL 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, 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 boolean type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_boolean(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in boolean type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_boolean(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesBoolean */
+
+/**
+ * @defgroup pluginsTypesDecimal64 Plugins: Decimal64 built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement decimal64 built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in decimal64 type.
+ */
+LIBYANG_API_DECL 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, 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 decimal64 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_decimal64(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in decimal64 type.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_decimal64(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesDecimal64 */
+
+/**
+ * @defgroup pluginsTypesEmpty Plugins: Empty built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement empty built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in empty type.
+ */
+LIBYANG_API_DECL 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, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/** @} pluginsTypesEmpty */
+
+/**
+ * @defgroup pluginsTypesEnumeration Plugins: Enumeration built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement enumeration built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in enumeration type.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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 */
+
+/**
+ * @defgroup pluginsTypesIdentityref Plugins: Identityref built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement identityref built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in identityref type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_compare_clb for the built-in identityref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in identityref type.
+ */
+LIBYANG_API_DECL 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);
+
+/** @} pluginsTypesIdentityref */
+
+/**
+ * @defgroup pluginsTypesInstanceid Plugins: Instance-identifier built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement instance-identifier built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_validate_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_compare_clb for the built-in instance-identifier type.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL 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.
+ */
+LIBYANG_API_DECL void lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesInstanceid */
+
+/**
+ * @defgroup pluginsTypesInteger Plugins: Integer built-in types callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement integer built-in types.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in signed integer types.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_compare_clb for the built-in signed integer types.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_int(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in signed integer types.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_int(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_store_clb for the built-in unsigned integer types.
+ */
+LIBYANG_API_DECL 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, 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 unsigned integer types.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in unsigned integer types.
+ */
+LIBYANG_API_DECL const void *lyplg_type_print_uint(const struct ly_ctx *ctx, const struct lyd_value *value,
+ LY_VALUE_FORMAT format, void *prefix_data, ly_bool *dynamic, size_t *value_len);
+
+/** @} pluginsTypesInteger */
+
+/**
+ * @defgroup pluginsTypesLeafref Plugins: Leafref built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement leafref built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_compare_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_dup_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_free_clb for the built-in leafref type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesLeafref */
+
+/**
+ * @defgroup pluginsTypesString Plugins: String built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement string built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in string type.
+ */
+LIBYANG_API_DECL 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, struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err);
+
+/** @} pluginsTypesString */
+
+/**
+ * @defgroup pluginsTypesUnion Plugins: Union built-in type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement union built-in type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the built-in union type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_compare_clb for the built-in union type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the built-in union type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_dup_clb for the built-in union type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the built-in union type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_free_clb for the built-in union type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesUnion */
+
+/**
+ * @defgroup pluginsTypesXpath10 Plugins: xpath1.0 `ietf-yang-types` type callbacks
+ * @ingroup pluginsTypes
+ * @{
+ *
+ * Callbacks used (besides the [simple callbacks](@ref pluginsTypesSimple)) to implement xpath1.0 derived type.
+ */
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_compare_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_compare_xpath10(const struct lyd_value *val1, const struct lyd_value *val2);
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL 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 Implementation of ::lyplg_type_dup_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_dup_xpath10(const struct ly_ctx *ctx, const struct lyd_value *original,
+ struct lyd_value *dup);
+
+/**
+ * @brief Implementation of ::lyplg_type_free_clb for the ietf-yang-types xpath1.0 type.
+ */
+LIBYANG_API_DECL void lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value);
+
+/** @} pluginsTypesXpath10 */
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] min Lower bound of the type.
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed integer value (optional).
+ * @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 parsing and validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_parse_int(const char *datatype, int base, int64_t min, int64_t max, const char *value,
+ size_t value_len, int64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Unsigned integer value parser and validator.
+ *
+ * @param[in] datatype Type of the unsigned integer for logging.
+ * @param[in] base Base of the integer's lexical representation. In case of built-in types, data must be represented in decimal format (base 10),
+ * but default values in schemas can be represented also as hexadecimal or octal values (base 0).
+ * @param[in] max Upper bound of the type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed unsigned integer value (optional).
+ * @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 parsing and validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_parse_uint(const char *datatype, int base, uint64_t max, const char *value,
+ size_t value_len, uint64_t *ret, struct ly_err_item **err);
+
+/**
+ * @brief Convert a string with a decimal64 value into libyang representation:
+ * ret = value * 10^fraction-digits
+ *
+ * @param[in] fraction_digits Fraction-digits of the decimal64 type.
+ * @param[in] value Value string to parse.
+ * @param[in] value_len Length of the @p value (mandatory parameter).
+ * @param[out] ret Parsed decimal64 value representing original value * 10^fraction-digits (optional).
+ * @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 parsing and validation.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_parse_dec64(uint8_t fraction_digits, const char *value, size_t value_len, int64_t *ret,
+ struct ly_err_item **err);
+
+/**
+ * @brief Decide if the @p derived identity is derived from (based on) the @p base identity.
+ *
+ * @param[in] base Expected base identity.
+ * @param[in] derived Expected derived identity.
+ * @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.
+ */
+LIBYANG_API_DECL 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.
+ *
+ * @param[in] basetype Base built-in type of the type with the range specified to get know if the @p range structure represents range or length restriction.
+ * @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.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_range(LY_DATA_TYPE basetype, struct lysc_range *range, int64_t value,
+ const char *strval, size_t strval_len, struct ly_err_item **err);
+
+/**
+ * @brief Data type validator for pattern-restricted string values.
+ *
+ * @param[in] patterns ([Sized array](@ref sizedarrays)) of the compiled list of pointers to the pattern restrictions.
+ * The array can be found in the ::lysc_type_str.patterns structure.
+ * @param[in] str String to validate.
+ * @param[in] str_len Length (number of bytes) of the string to validate (mandatory).
+ * @param[out] err Error information in case of failure or non-matching @p str. The error structure can be freed by ::ly_err_free().
+ * @return LY_SUCCESS when @p matches all the patterns.
+ * @return LY_EVALID when @p does not match any of the patterns.
+ * @return LY_ESYS in case of PCRE2 error.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_validate_patterns(struct lysc_pattern **patterns, const char *str, size_t str_len,
+ struct ly_err_item **err);
+
+/**
+ * @brief Find leafref target in data.
+ *
+ * @param[in] lref Leafref type.
+ * @param[in] node Context node.
+ * @param[in] value Target value.
+ * @param[in] tree Full data tree to search in.
+ * @param[out] target Optional found target.
+ * @param[out] errmsg Error message in case of error.
+ * @return LY_ERR value.
+ */
+LIBYANG_API_DECL LY_ERR lyplg_type_resolve_leafref(const struct lysc_type_leafref *lref, const struct lyd_node *node,
+ struct lyd_value *value, const struct lyd_node *tree, struct lyd_node **target, char **errmsg);
+
+/** @} pluginsTypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LY_PLUGINS_TYPES_H_ */
diff --git a/src/plugins_types/binary.c b/src/plugins_types/binary.c
new file mode 100644
index 0000000..519ec2e
--- /dev/null
+++ b/src/plugins_types/binary.c
@@ -0,0 +1,466 @@
+/**
+ * @file binary.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in binary type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection 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);
+ if (!(*str_len)) {
+ **str = 0;
+ return LY_SUCCESS;
+ }
+
+ ptr = *str;
+ for (i = 0; i + 2 < size; 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;
+}
+
+/**
+ * @brief Remove all newlines from a base64 string if present.
+ *
+ * @param[in,out] value Value, may be dynamic and modified.
+ * @param[in,out] value_len Length of @p value, is updated.
+ * @param[in,out] options Type options, are updated.
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+binary_base64_newlines(char **value, size_t *value_len, uint32_t *options, struct ly_err_item **err)
+{
+ char *val;
+ size_t len;
+
+ if ((*value_len < 65) || ((*value)[64] != '\n')) {
+ /* no newlines */
+ return LY_SUCCESS;
+ }
+
+ if (!(*options & LYPLG_TYPE_STORE_DYNAMIC)) {
+ /* make the value dynamic so we can modify it */
+ *value = strndup(*value, *value_len);
+ LY_CHECK_RET(!*value, LY_EMEM);
+ *options |= LYPLG_TYPE_STORE_DYNAMIC;
+ }
+
+ val = *value;
+ len = *value_len;
+ while (len > 64) {
+ if (val[64] != '\n') {
+ /* missing, error */
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Newlines are expected every 64 Base64 characters.");
+ }
+
+ /* remove the newline */
+ memmove(val + 64, val + 65, len - 64);
+ --(*value_len);
+ val += 64;
+ len -= 65;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF 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 *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_bin *type_bin = (struct lysc_type_bin *)type;
+ struct lyd_value_binary *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* store value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val->data = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else if (value_len) {
+ val->data = malloc(value_len);
+ LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
+ memcpy(val->data, value, value_len);
+ } else {
+ val->data = strdup("");
+ LY_CHECK_ERR_GOTO(!val->data, ret = LY_EMEM, cleanup);
+ }
+
+ /* 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, cleanup);
+
+ if (format != LY_VALUE_CANON) {
+ /* accept newline every 64 characters (PEM data) */
+ ret = binary_base64_newlines((char **)&value, &value_len, &options, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* validate */
+ ret = binary_base64_validate(value, value_len, type_bin, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* get the binary value */
+ ret = binary_base64_decode(value, value_len, &val->data, &val->size);
+ 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_binary(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_binary(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_binary *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if ((v1->size != v2->size) || memcmp(v1->data, v2->data, v1->size)) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF 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;
+ char *ret;
+ size_t ret_len = 0;
+
+ LYD_VALUE_GET(value, val);
+
+ 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;
+}
+
+LIBYANG_API_DEF 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, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ dup_val->data = orig_val->size ? malloc(orig_val->size) : strdup("");
+ LY_CHECK_ERR_GOTO(!dup_val->data, ret = LY_EMEM, error);
+
+ memcpy(dup_val->data, orig_val->data, orig_val->size);
+ dup_val->size = orig_val->size;
+ dup->realtype = original->realtype;
+
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_binary(ctx, dup);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_binary(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_binary *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ free(val->data);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for binray type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_binary[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_BINARY_STR,
+
+ .plugin.id = "libyang 2 - binary, version 1",
+ .plugin.store = lyplg_type_store_binary,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_binary,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_binary,
+ .plugin.duplicate = lyplg_type_dup_binary,
+ .plugin.free = lyplg_type_free_binary,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/bits.c b/src/plugins_types/bits.c
new file mode 100644
index 0000000..04adace
--- /dev/null
+++ b/src/plugins_types/bits.c
@@ -0,0 +1,510 @@
+/**
+ * @file bits.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in bits type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBits bits (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | returned by ::lyplg_type_bits_bitmap_size() | yes | pointer to integer type of the specific size, if size more than 8 use `char *` | bitmap of the set bits |
+ */
+
+/**
+ * @brief Get the position of the last bit.
+ */
+#define BITS_LAST_BIT_POSITION(type_bits) (type_bits->bits[LY_ARRAY_COUNT(type_bits->bits) - 1].position)
+
+/**
+ * @brief Get a specific byte in a bitmap.
+ */
+#ifdef IS_BIG_ENDIAN
+# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + (size - 1) - idx)
+#else
+# define BITS_BITMAP_BYTE(bitmap, size, idx) (bitmap + idx)
+#endif
+
+LIBYANG_API_DEF size_t
+lyplg_type_bits_bitmap_size(const struct lysc_type_bits *type)
+{
+ size_t needed_bytes, size;
+
+ LY_CHECK_ARG_RET(NULL, type, type->basetype == LY_TYPE_BITS, 0);
+
+ /* minimum needed bytes to hold all the bit positions (which start at 0) */
+ needed_bytes = ((BITS_LAST_BIT_POSITION(type) + 1) / 8) + ((BITS_LAST_BIT_POSITION(type) + 1) % 8 ? 1 : 0);
+ LY_CHECK_ERR_RET(!needed_bytes, LOGINT(NULL), 0);
+
+ if ((needed_bytes == 1) || (needed_bytes == 2)) {
+ /* uint8_t or uint16_t */
+ size = needed_bytes;
+ } else if (needed_bytes < 5) {
+ /* uint32_t */
+ size = 4;
+ } else if (needed_bytes < 9) {
+ /* uint64_t */
+ size = 8;
+ } else {
+ /* no basic type, do not round */
+ size = needed_bytes;
+ }
+
+ return size;
+}
+
+LIBYANG_API_DEF ly_bool
+lyplg_type_bits_is_bit_set(const char *bitmap, size_t size, uint32_t bit_position)
+{
+ char bitmask;
+
+ /* find the byte with our bit */
+ (void)size;
+ bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8);
+ bit_position %= 8;
+
+ /* generate bitmask */
+ bitmask = 1;
+ bitmask <<= bit_position;
+
+ /* check if bit set */
+ if (*bitmap & bitmask) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Set bit at a specific position.
+ *
+ * @param[in,out] bitmap Bitmap to modify.
+ * @param[in] size Size of @p bitmap.
+ * @param[in] bit_position Bit position to set.
+ */
+static void
+bits_bit_set(char *bitmap, size_t size, uint32_t bit_position)
+{
+ char bitmask;
+
+ /* find the byte with our bit */
+ (void)size;
+ bitmap = BITS_BITMAP_BYTE(bitmap, size, bit_position / 8);
+ bit_position %= 8;
+
+ /* generate bitmask */
+ bitmask = 1;
+ bitmask <<= bit_position;
+
+ /* set the bit */
+ *bitmap |= bitmask;
+}
+
+/**
+ * @brief Convert a list of bit names separated by whitespaces to a bitmap.
+ *
+ * @param[in] value Value to convert.
+ * @param[in] value_len Length of @p value.
+ * @param[in] type Type of the value.
+ * @param[in,out] bitmap Zeroed bitmap, is filled (set).
+ * @param[out] err Error information.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+bits_str2bitmap(const char *value, size_t value_len, struct lysc_type_bits *type, char *bitmap, struct ly_err_item **err)
+{
+ size_t idx_start, idx_end;
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool found;
+
+ idx_start = idx_end = 0;
+ while (idx_end < value_len) {
+ /* skip whitespaces */
+ while ((idx_end < value_len) && isspace(value[idx_end])) {
+ ++idx_end;
+ }
+ if (idx_end == value_len) {
+ break;
+ }
+
+ /* parse bit name */
+ idx_start = idx_end;
+ while ((idx_end < value_len) && !isspace(value[idx_end])) {
+ ++idx_end;
+ }
+
+ /* find the bit */
+ found = 0;
+ LY_ARRAY_FOR(type->bits, u) {
+ if (!ly_strncmp(type->bits[u].name, value + idx_start, idx_end - idx_start)) {
+ found = 1;
+ break;
+ }
+ }
+
+ /* check if name exists */
+ if (!found) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid bit \"%.*s\".", (int)(idx_end - idx_start),
+ value + idx_start);
+ }
+
+ /* check for duplication */
+ if (lyplg_type_bits_is_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position)) {
+ return ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Duplicate bit \"%s\".", type->bits[u].name);
+ }
+
+ /* set the bit */
+ bits_bit_set(bitmap, lyplg_type_bits_bitmap_size(type), type->bits[u].position);
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Add a bit item into an array.
+ *
+ * @param[in] position Bit position to add.
+ * @param[in] type Bitis type to read the bit positions and names from.
+ * @param[in,out] items Array of bit item pointers to add to.
+ */
+static void
+bits_add_item(uint32_t position, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items)
+{
+ LY_ARRAY_COUNT_TYPE u;
+
+ /* find the bit item */
+ LY_ARRAY_FOR(type->bits, u) {
+ if (type->bits[u].position == position) {
+ break;
+ }
+ }
+
+ /* add it at the end */
+ items[LY_ARRAY_COUNT(items)] = &type->bits[u];
+ LY_ARRAY_INCREMENT(items);
+}
+
+/**
+ * @brief Convert a bitmap to a sized array of pointers to their bit definitions.
+ *
+ * @param[in] bitmap Bitmap to read from.
+ * @param[in] type Bits type.
+ * @param[in,out] items Allocated sized array to fill with the set bits.
+ */
+static void
+bits_bitmap2items(const char *bitmap, struct lysc_type_bits *type, struct lysc_type_bitenum_item **items)
+{
+ size_t i, bitmap_size = lyplg_type_bits_bitmap_size(type);
+ uint32_t bit_pos;
+ uint8_t bitmask;
+ const uint8_t *byte;
+
+ bit_pos = 0;
+ for (i = 0; i < bitmap_size; ++i) {
+ /* check this byte (but not necessarily all bits in the last byte) */
+ byte = (uint8_t *)BITS_BITMAP_BYTE(bitmap, bitmap_size, i);
+ for (bitmask = 1; bitmask; bitmask <<= 1) {
+ if (*byte & bitmask) {
+ /* add this bit */
+ bits_add_item(bit_pos, type, items);
+ }
+
+ if (bit_pos == BITS_LAST_BIT_POSITION(type)) {
+ /* we have checked the last valid bit */
+ break;
+ }
+
+ ++bit_pos;
+ }
+ }
+}
+
+/**
+ * @brief Generate canonical value from ordered array of set bit items.
+ *
+ * @param[in] items Sized array of set bit items.
+ * @param[out] canonical Canonical string value.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+bits_items2canon(struct lysc_type_bitenum_item **items, char **canonical)
+{
+ char *ret;
+ size_t ret_len;
+ LY_ARRAY_COUNT_TYPE u;
+
+ *canonical = NULL;
+
+ /* init value */
+ ret = strdup("");
+ LY_CHECK_RET(!ret, LY_EMEM);
+ ret_len = 0;
+
+ LY_ARRAY_FOR(items, u) {
+ if (!ret_len) {
+ ret = ly_realloc(ret, strlen(items[u]->name) + 1);
+ LY_CHECK_RET(!ret, LY_EMEM);
+ strcpy(ret, items[u]->name);
+
+ ret_len = strlen(ret);
+ } else {
+ ret = ly_realloc(ret, ret_len + 1 + strlen(items[u]->name) + 1);
+ LY_CHECK_RET(!ret, LY_EMEM);
+ sprintf(ret + ret_len, " %s", items[u]->name);
+
+ ret_len += 1 + strlen(items[u]->name);
+ }
+ }
+
+ *canonical = ret;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_bits(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type;
+ struct lyd_value_bits *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != lyplg_type_bits_bitmap_size(type_bits)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB bits value size %zu (expected %zu).",
+ value_len, lyplg_type_bits_bitmap_size(type_bits));
+ goto cleanup;
+ }
+
+ /* store value (bitmap) */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ val->bitmap = (char *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ val->bitmap = malloc(value_len);
+ LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
+ memcpy(val->bitmap, value, value_len);
+ }
+
+ /* allocate and fill the bit item array */
+ LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup);
+ bits_bitmap2items(val->bitmap, type_bits, val->items);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate the bitmap */
+ val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
+ LY_CHECK_ERR_GOTO(!val->bitmap, ret = LY_EMEM, cleanup);
+ memset(val->bitmap, 0, lyplg_type_bits_bitmap_size(type_bits));
+
+ /* fill the bitmap */
+ ret = bits_str2bitmap(value, value_len, type_bits, val->bitmap, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* allocate and fill the bit item array */
+ LY_ARRAY_CREATE_GOTO(ctx, val->items, LY_ARRAY_COUNT(type_bits->bits), ret, cleanup);
+ bits_bitmap2items(val->bitmap, type_bits, val->items);
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value_len ? value : "", value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret != LY_SUCCESS, cleanup);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_bits(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_bits(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ struct lyd_value_bits *v1, *v2;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)val1->realtype;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(v1->bitmap, v2->bitmap, lyplg_type_bits_bitmap_size(type_bits))) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_bits(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)value->realtype;
+ struct lyd_value_bits *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = lyplg_type_bits_bitmap_size(type_bits);
+ }
+ return val->bitmap;
+ }
+
+ /* generate canonical value if not already */
+ if (!value->_canonical) {
+ /* get the canonical value */
+ if (bits_items2canon(val->items, &ret)) {
+ return NULL;
+ }
+
+ /* store it */
+ if (lydict_insert_zc(ctx, ret, (const char **)&value->_canonical)) {
+ LOGMEM(ctx);
+ return NULL;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_bits(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+ struct lysc_type_bits *type_bits = (struct lysc_type_bits *)original->realtype;
+ LY_ARRAY_COUNT_TYPE u;
+ struct lyd_value_bits *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ /* duplicate bitmap */
+ dup_val->bitmap = malloc(lyplg_type_bits_bitmap_size(type_bits));
+ LY_CHECK_ERR_GOTO(!dup_val->bitmap, ret = LY_EMEM, error);
+ memcpy(dup_val->bitmap, orig_val->bitmap, lyplg_type_bits_bitmap_size(type_bits));
+
+ /* duplicate bit item pointers */
+ LY_ARRAY_CREATE_GOTO(ctx, dup_val->items, LY_ARRAY_COUNT(orig_val->items), ret, error);
+ LY_ARRAY_FOR(orig_val->items, u) {
+ LY_ARRAY_INCREMENT(dup_val->items);
+ dup_val->items[u] = orig_val->items[u];
+ }
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_bits(ctx, dup);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_bits(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_bits *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ free(val->bitmap);
+ LY_ARRAY_FREE(val->items);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for bits type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_bits[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_BITS_STR,
+
+ .plugin.id = "libyang 2 - bits, version 1",
+ .plugin.store = lyplg_type_store_bits,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_bits,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_bits,
+ .plugin.duplicate = lyplg_type_dup_bits,
+ .plugin.free = lyplg_type_free_bits,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/boolean.c b/src/plugins_types/boolean.c
new file mode 100644
index 0000000..f8b19f6
--- /dev/null
+++ b/src/plugins_types/boolean.c
@@ -0,0 +1,165 @@
+/**
+ * @file boolean.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in boolean 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
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesBoolean boolean (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 1 | yes | `int8_t *` | 0 for false, otherwise true |
+ */
+
+LIBYANG_API_DEF 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 *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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ 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;
+ }
+
+ /* store value */
+ i = *(int8_t *)value;
+ storage->boolean = i ? 1 : 0;
+
+ /* store canonical value, it always is */
+ 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, cleanup);
+
+ /* validate and store 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"))) {
+ i = 0;
+ } else {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid boolean value \"%.*s\".", (int)value_len,
+ (char *)value);
+ goto cleanup;
+ }
+ storage->boolean = i;
+
+ /* 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, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((char *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_boolean(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->boolean != val2->boolean) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_boolean(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 = sizeof value->boolean;
+ }
+ return &value->boolean;
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for boolean type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_boolean[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_BOOL_STR,
+
+ .plugin.id = "libyang 2 - boolean, version 1",
+ .plugin.store = lyplg_type_store_boolean,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_boolean,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_boolean,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/date_and_time.c b/src/plugins_types/date_and_time.c
new file mode 100644
index 0000000..5ccb86d
--- /dev/null
+++ b/src/plugins_types/date_and_time.c
@@ -0,0 +1,339 @@
+/**
+ * @file date_and_time.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-yang-types date-and-time type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDateAndTime date-and-time (ietf-yang-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `time_t *` | UNIX timestamp |
+ * | 1 | no | `int8_t *` | flag whether the value is in the special -00:00 unknown timezone or not |
+ * | 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 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 *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_dat = (struct lysc_type_str *)type;
+ struct lyd_value_date_and_time *val;
+ struct tm tm;
+ uint32_t i;
+ char c;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 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 = 9; 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;
+ }
+ }
+
+ /* store timestamp */
+ memcpy(&val->time, value, sizeof val->time);
+
+ /* store fractions of second */
+ if (value_len > 9) {
+ val->fractions_s = strndup(((char *)value) + 9, value_len - 9);
+ LY_CHECK_ERR_GOTO(!val->fractions_s, ret = LY_EMEM, cleanup);
+ }
+
+ /* store unknown timezone */
+ if (value_len > 8) {
+ val->unknown_tz = *(((int8_t *)value) + 8) ? 1 : 0;
+ }
+
+ /* 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, 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);
+ }
+
+ /* date-and-time pattern */
+ ret = lyplg_type_validate_patterns(type_dat->patterns, value, value_len, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* pattern validation succeeded, convert to UNIX time and fractions of second */
+ ret = ly_time_str2time(value, &val->time, &val->fractions_s);
+ LY_CHECK_GOTO(ret, cleanup);
+
+#ifdef HAVE_TIME_H_TIMEZONE
+ if (!strncmp(((char *)value + value_len) - 6, "-00:00", 6)) {
+ /* unknown timezone, move the timestamp to UTC */
+ tzset();
+ val->time += (time_t)timezone;
+ val->unknown_tz = 1;
+
+ /* DST may apply, adjust accordingly */
+ if (!localtime_r(&val->time, &tm)) {
+ ret = ly_err_new(err, LY_ESYS, LYVE_DATA, NULL, NULL, "localtime_r() call failed (%s).", strerror(errno));
+ goto cleanup;
+ } else if (tm.tm_isdst < 0) {
+ ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Failed to get DST information.");
+ goto cleanup;
+ }
+ if (tm.tm_isdst) {
+ /* move an hour back */
+ val->time -= 3600;
+ }
+ }
+#else
+ (void)tm;
+ val->unknown_tz = 1;
+#endif
+
+ 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);
+ }
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ 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, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ /* compare timestamp and unknown tz */
+ if ((v1->time != v2->time) || (v1->unknown_tz != v2->unknown_tz)) {
+ 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;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ if (val->unknown_tz || val->fractions_s) {
+ ret = malloc(8 + 1 + (val->fractions_s ? strlen(val->fractions_s) : 0));
+ LY_CHECK_ERR_RET(!ret, LOGMEM(ctx), NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = 8 + 1 + (val->fractions_s ? strlen(val->fractions_s) : 0);
+ }
+ memcpy(ret, &val->time, sizeof val->time);
+ memcpy(ret + 8, &val->unknown_tz, sizeof val->unknown_tz);
+ if (val->fractions_s) {
+ memcpy(ret + 9, 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 (ly_time_time2str(val->time, val->fractions_s, &ret)) {
+ return NULL;
+ }
+
+ if (val->unknown_tz) {
+ /* date and time is correct, fix only the timezone */
+ strcpy((ret + strlen(ret)) - 6, "-00:00");
+ }
+
+ /* 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_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, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* optional canonical value */
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ /* copy timestamp and unknown tz */
+ dup_val->time = orig_val->time;
+ dup_val->unknown_tz = orig_val->unknown_tz;
+
+ /* 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->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;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ free(val->fractions_s);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for date-and-time type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_date_and_time[] = {
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "date-and-time",
+
+ .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_date_and_time,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_date_and_time,
+ .plugin.duplicate = lyplg_type_dup_date_and_time,
+ .plugin.free = lyplg_type_free_date_and_time,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/decimal64.c b/src/plugins_types/decimal64.c
new file mode 100644
index 0000000..25a88d9
--- /dev/null
+++ b/src/plugins_types/decimal64.c
@@ -0,0 +1,239 @@
+/**
+ * @file decimal64.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in decimal64 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
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesDecimal64 decimal64 (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 8 | yes | `int64_t *` | little-endian 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)
+{
+ char *ret;
+
+ /* allocate the value */
+ ret = calloc(1, LY_NUMBER_MAXLEN);
+ LY_CHECK_RET(!ret, LY_EMEM);
+
+ 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(ret, "%0*" PRId64 " ", (num > 0) ? (type->fraction_digits + 1) : (type->fraction_digits + 2), num);
+ }
+ 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 */
+ ret[count - 1] = '\0';
+ } else {
+ j = 0;
+ ret[count - 1] = ret[count - 2];
+ }
+ count--;
+ }
+ ret[count - 1] = '.';
+ } else {
+ /* zero */
+ sprintf(ret, "0.0");
+ }
+
+ *str = ret;
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF 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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ 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, in host byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ } 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);
+ }
+
+ /* store value */
+ storage->dec64 = num;
+
+ /* we need canonical value for the range check */
+ 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((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_decimal64(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ /* if type is the same, the fraction digits are, too */
+ if (val1->dec64 != val2->dec64) {
+ return LY_ENOT;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_decimal64(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ int64_t num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ num = htole64(value->dec64);
+ if (num == value->dec64) {
+ /* values are equal, little-endian */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = sizeof value->dec64;
+ }
+ return &value->dec64;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, sizeof value->dec64);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = sizeof value->dec64;
+ }
+ memcpy(buf, &num, sizeof value->dec64);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for decimal64 type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_decimal64[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_DEC64_STR,
+
+ .plugin.id = "libyang 2 - decimal64, version 1",
+ .plugin.store = lyplg_type_store_decimal64,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_decimal64,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_decimal64,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 8,
+ },
+ {0}
+};
diff --git a/src/plugins_types/empty.c b/src/plugins_types/empty.c
new file mode 100644
index 0000000..d84634f
--- /dev/null
+++ b/src/plugins_types/empty.c
@@ -0,0 +1,103 @@
+/**
+ * @file empty.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in empty 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
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEmpty empty (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 0 | yes | `void` | none |
+ */
+
+LIBYANG_API_DEF 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,
+ 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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* validation */
+ if (value_len) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid empty value length %zu.", value_len);
+ goto 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, &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;
+}
+
+/**
+ * @brief Plugin information for empty type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_empty[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_EMPTY_STR,
+
+ .plugin.id = "libyang 2 - empty, version 1",
+ .plugin.store = lyplg_type_store_empty,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 0,
+ },
+ {0}
+};
diff --git a/src/plugins_types/enumeration.c b/src/plugins_types/enumeration.c
new file mode 100644
index 0000000..91ca822
--- /dev/null
+++ b/src/plugins_types/enumeration.c
@@ -0,0 +1,202 @@
+/**
+ * @file enumeration.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in enumeration type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesEnumeration enumeration (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `int32 *` | assigned little-endian value of the enum |
+ */
+
+LIBYANG_API_DEF 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 *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;
+ ly_bool found = 0;
+ int64_t num = 0;
+ int32_t num_val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ 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;
+ }
+
+ /* convert the value to host byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ num_val = num;
+
+ /* find the matching enumeration value item */
+ LY_ARRAY_FOR(type_enum->enums, u) {
+ if (type_enum->enums[u].value == num_val) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /* value not found */
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid enumeration value % " PRIi32 ".", num_val);
+ goto cleanup;
+ }
+
+ /* store value */
+ storage->enum_item = &type_enum->enums[u];
+
+ /* canonical settings via dictionary due to free callback */
+ ret = lydict_insert(ctx, type_enum->enums[u].name, 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, 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;
+ }
+
+ /* store value */
+ storage->enum_item = &type_enum->enums[u];
+
+ /* 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, 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;
+}
+
+LIBYANG_API_DEF 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)
+{
+ int64_t prev_num = 0, num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ prev_num = num = value->enum_item->value;
+ num = htole64(num);
+ if (num == prev_num) {
+ /* values are equal, little-endian */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 4;
+ }
+ return &value->enum_item->value;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, 4);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = 4;
+ }
+ memcpy(buf, &num, 4);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for enumeration type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_enumeration[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_ENUM_STR,
+
+ .plugin.id = "libyang 2 - enumeration, version 1",
+ .plugin.store = lyplg_type_store_enum,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_enum,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ },
+ {0}
+};
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
new file mode 100644
index 0000000..8b7985d
--- /dev/null
+++ b/src/plugins_types/identityref.c
@@ -0,0 +1,352 @@
+/**
+ * @file identityref.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in identityref 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 */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesIdentityref identityref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the identityref |
+ */
+
+/**
+ * @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;
+}
+
+/**
+ * @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)
+{
+ 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 *id, *identities;
+
+ /* locate prefix if any */
+ for (prefix_len = 0; (prefix_len < value_len) && (value[prefix_len] != ':'); ++prefix_len) {}
+ if (prefix_len < value_len) {
+ id_name = &value[prefix_len + 1];
+ id_len = value_len - (prefix_len + 1);
+ } else {
+ prefix_len = 0;
+ id_name = value;
+ id_len = value_len;
+ }
+
+ if (!id_len) {
+ 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) {
+ 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) {
+ if (!ly_strncmp(identities[u].name, id_name, id_len)) {
+ /* we have match */
+ id = &identities[u];
+ break;
+ }
+ }
+ if (!id) {
+ /* no match */
+ 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, mod->name);
+ }
+
+ *ident = id;
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check that an identityref is derived from the type base.
+ *
+ * @param[in] ident Derived identity to which identityref points.
+ * @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;
+ }
+ }
+
+ /* it does not, generate a nice error */
+ if (u == LY_ARRAY_COUNT(type->bases)) {
+ str = NULL;
+ str_len = 1;
+ 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);
+ }
+
+ /* no match */
+ 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);
+ } 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);
+ }
+ free(str);
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+/**
+ * @brief Check if @p ident is not disabled.
+ *
+ * Identity is disabled if it is located in an unimplemented model or
+ * it can be disabled by if-feature. Calling this function may invoke
+ * the implementation of another module.
+ *
+ * @param[in] ident Derived identity to which identityref points.
+ * @param[in] value Value of identityref.
+ * @param[in] value_len Length (number of bytes) of the given @p value.
+ * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_check_ident(const struct lysc_ident *ident, const char *value,
+ size_t value_len, uint32_t options, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (!ident->module->implemented) {
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ ret = lyplg_type_make_implemented(ident->module, NULL, unres);
+ } 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);
+ }
+ } else if (lys_identity_iffeature_value(ident) == LY_ENOT) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity is disabled by if-feature.",
+ (int)value_len, value);
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF 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 = NULL;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* 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);
+
+ /* check if the identity is enabled */
+ ret = identityref_check_ident(ident, value, value_len, options, unres, err);
+ LY_CHECK_GOTO(ret, 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);
+
+ if (ctx_node) {
+ /* check status */
+ ret = lyplg_type_check_status(ctx_node, ident->flags, format, prefix_data, ident->name, err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* store value */
+ storage->ident = ident;
+
+ /* 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((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_identityref(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->ident == val2->ident) {
+ return LY_SUCCESS;
+ }
+ return LY_ENOT;
+}
+
+LIBYANG_API_DEF const void *
+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 *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 (identityref_ident2str(value->ident, format, prefix_data, &ret, value_len)) {
+ return NULL;
+ }
+ *dynamic = 1;
+ return ret;
+}
+
+/**
+ * @brief Plugin information for identityref type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_identityref[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_IDENT_STR,
+
+ .plugin.id = "libyang 2 - identityref, version 1",
+ .plugin.store = lyplg_type_store_identityref,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_identityref,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_identityref,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c
new file mode 100644
index 0000000..d151d0a
--- /dev/null
+++ b/src/plugins_types/instanceid.c
@@ -0,0 +1,382 @@
+/**
+ * @file instanceid.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in instance-identifier type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "path.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @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, quot;
+ const struct lys_module *mod = NULL;
+ ly_bool inherit_prefix = 0, d;
+ const char *strval;
+
+ switch (format) {
+ case LY_VALUE_XML:
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* everything is prefixed */
+ inherit_prefix = 0;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* the same prefix is inherited and skipped */
+ inherit_prefix = 1;
+ 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);
+
+ /* 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 {
+ 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);
+ }
+ 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);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ ret = ly_strcat(&result, "[.=%c%s%c]", quot, strval, quot);
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ }
+
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ free(result);
+ } else {
+ *str = result;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF 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)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)type;
+ struct ly_path *path;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* compile instance-identifier into path */
+ if (format == LY_VALUE_LYB) {
+ /* The @p value in LYB format is the same as in JSON format. */
+ ret = lyplg_type_lypath_new(ctx, value, value_len, options, LY_VALUE_JSON, prefix_data, ctx_node,
+ unres, &path, err);
+ } else {
+ ret = lyplg_type_lypath_new(ctx, value, value_len, options, format, prefix_data, ctx_node,
+ unres, &path, err);
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store value */
+ storage->target = path;
+
+ /* check status */
+ ret = lyplg_type_lypath_check_status(ctx_node, path, format, prefix_data, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* 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);
+
+ 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_instanceid(ctx, storage);
+ }
+ if (!ret && type_inst->require_instance) {
+ /* needs to be resolved */
+ return LY_EINCOMPLETE;
+ } else {
+ return ret;
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_instanceid(const struct ly_ctx *ctx, const struct lysc_type *UNUSED(type),
+ const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lyd_value *storage,
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_instanceid *type_inst = (struct lysc_type_instanceid *)storage->realtype;
+ const char *value;
+ char *path;
+
+ *err = NULL;
+
+ if (!type_inst->require_instance) {
+ /* redundant to resolve */
+ return LY_SUCCESS;
+ }
+
+ /* find the target in data */
+ if ((ret = ly_path_eval(storage->target, tree, NULL))) {
+ value = lyplg_type_print_instanceid(ctx, storage, LY_VALUE_CANON, NULL, NULL, NULL);
+ path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
+ return ly_err_new(err, ret, LYVE_DATA, path, strdup("instance-required"), LY_ERRMSG_NOINST, value);
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_instanceid(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ LY_ARRAY_COUNT_TYPE u, v;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1 == val2) {
+ return LY_SUCCESS;
+ } else if (LY_ARRAY_COUNT(val1->target) != LY_ARRAY_COUNT(val2->target)) {
+ return LY_ENOT;
+ }
+
+ LY_ARRAY_FOR(val1->target, u) {
+ struct ly_path *s1 = &val1->target[u];
+ struct ly_path *s2 = &val2->target[u];
+
+ if ((s1->node != s2->node) || (s1->pred_type != s2->pred_type) ||
+ (s1->predicates && (LY_ARRAY_COUNT(s1->predicates) != LY_ARRAY_COUNT(s2->predicates)))) {
+ return LY_ENOT;
+ }
+ if (s1->predicates) {
+ LY_ARRAY_FOR(s1->predicates, v) {
+ struct ly_path_predicate *pred1 = &s1->predicates[v];
+ struct ly_path_predicate *pred2 = &s2->predicates[v];
+
+ switch (s1->pred_type) {
+ case LY_PATH_PREDTYPE_NONE:
+ break;
+ case LY_PATH_PREDTYPE_POSITION:
+ /* position predicate */
+ if (pred1->position != pred2->position) {
+ return LY_ENOT;
+ }
+ 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)) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_PATH_PREDTYPE_LEAFLIST:
+ /* leaf-list predicate */
+ if (((struct lysc_node_leaflist *)s1->node)->type->plugin->compare(&pred1->value, &pred2->value)) {
+ return LY_ENOT;
+ }
+ }
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF 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;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_instanceid(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret;
+
+ memset(dup, 0, sizeof *dup);
+
+ /* canonical value */
+ ret = lydict_insert(ctx, original->_canonical, 0, &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_SUCCESS;
+
+error:
+ lyplg_type_free_instanceid(ctx, dup);
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_instanceid(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ ly_path_free(ctx, value->target);
+}
+
+/**
+ * @brief Plugin information for instance-identifier type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_instanceid[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INST_STR,
+
+ .plugin.id = "libyang 2 - instance-identifier, version 1",
+ .plugin.store = lyplg_type_store_instanceid,
+ .plugin.validate = lyplg_type_validate_instanceid,
+ .plugin.compare = lyplg_type_compare_instanceid,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_instanceid,
+ .plugin.duplicate = lyplg_type_dup_instanceid,
+ .plugin.free = lyplg_type_free_instanceid,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c
new file mode 100644
index 0000000..0cd08f7
--- /dev/null
+++ b/src/plugins_types/instanceid_keys.c
@@ -0,0 +1,229 @@
+/**
+ * @file instanceid_keys.c
+ * @author Michal Basko <mvasko@cesnet.cz>
+ * @brief ietf-netconf edit-config key metadata instance-identifier keys predicate type plugin.
+ *
+ * Copyright (c) 2022 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "path.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+#include "xpath.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInstanceIdentifierKeys instance-identifier-keys (yang)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier keys predicate |
+ */
+
+/**
+ * @brief Special lyd_value structure for yang instance-identifier-keys values.
+ */
+struct lyd_value_instance_identifier_keys {
+ struct lyxp_expr *keys;
+ const struct ly_ctx *ctx;
+ void *prefix_data;
+ LY_VALUE_FORMAT format;
+};
+
+/**
+ * @brief Print instance-id-keys value in the specific format.
+ *
+ * @param[in] val instance-id-keys value structure.
+ * @param[in] format Format to print in.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[out] str_value Printed value.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+instanceid_keys_print_value(const struct lyd_value_instance_identifier_keys *val, LY_VALUE_FORMAT format, void *prefix_data,
+ char **str_value, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint32_t cur_idx = 0, str_len = 0;
+ enum lyxp_token cur_tok;
+ char *str_tok;
+ void *mem;
+ const char *cur_exp_ptr;
+ ly_bool is_nt;
+ const struct lys_module *context_mod = NULL;
+
+ *str_value = NULL;
+
+ while (cur_idx < val->keys->used) {
+ cur_tok = val->keys->tokens[cur_idx];
+ cur_exp_ptr = val->keys->expr + val->keys->tok_pos[cur_idx];
+
+ if ((cur_tok == LYXP_TOKEN_NAMETEST) || (cur_tok == LYXP_TOKEN_LITERAL)) {
+ /* tokens that may include prefixes, get them in the target format */
+ is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
+ LY_CHECK_GOTO(ret = lyplg_type_xpath10_print_token(cur_exp_ptr, val->keys->tok_len[cur_idx], is_nt, &context_mod,
+ val->ctx, val->format, val->prefix_data, format, prefix_data, &str_tok, err), error);
+
+ /* append the converted token */
+ mem = realloc(*str_value, str_len + strlen(str_tok) + 1);
+ LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ *str_value = mem;
+ str_len += sprintf(*str_value + str_len, "%s", str_tok);
+ free(str_tok);
+
+ /* token processed */
+ ++cur_idx;
+ } else {
+ /* just copy the token */
+ mem = realloc(*str_value, str_len + val->keys->tok_len[cur_idx] + 1);
+ LY_CHECK_GOTO(!mem, error_mem);
+ *str_value = mem;
+ str_len += sprintf(*str_value + str_len, "%.*s", (int)val->keys->tok_len[cur_idx], cur_exp_ptr);
+
+ /* token processed */
+ ++cur_idx;
+ }
+ }
+
+ return LY_SUCCESS;
+
+error_mem:
+ ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+
+error:
+ free(*str_value);
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the instance-identifier-keys yang type.
+ */
+static LY_ERR
+lyplg_type_store_instanceid_keys(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 *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_instance_identifier_keys *val;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ /* 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);
+
+ /* parse instance-identifier keys, with optional prefix even though it should be mandatory */
+ if (value_len && (((char *)value)[0] != '[')) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid first character '%c', list key predicates expected.",
+ ((char *)value)[0]);
+ goto cleanup;
+ }
+ ret = ly_path_parse_predicate(ctx, NULL, value_len ? value : "", value_len, LY_PATH_PREFIX_OPTIONAL,
+ LY_PATH_PRED_KEYS, &val->keys);
+ if (ret) {
+ ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL, "%s", ly_errmsg(ctx));
+ goto cleanup;
+ }
+ val->ctx = ctx;
+
+ /* store format-specific data and context for later prefix resolution */
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ switch (format) {
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* 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);
+ }
+ break;
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ /* JSON format with prefix is the canonical one */
+ ret = instanceid_keys_print_value(val, LY_VALUE_JSON, NULL, &canon, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ break;
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_xpath10(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for instance-identifier type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_instanceid_keys[] = {
+ {
+ .module = "yang",
+ .revision = NULL,
+ .name = "instance-identifier-keys",
+
+ .plugin.id = "libyang 2 - instance-identifier-keys, version 1",
+ .plugin.store = lyplg_type_store_instanceid_keys,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_xpath10,
+ .plugin.duplicate = lyplg_type_dup_xpath10,
+ .plugin.free = lyplg_type_free_xpath10,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/integer.c b/src/plugins_types/integer.c
new file mode 100644
index 0000000..0903365
--- /dev/null
+++ b/src/plugins_types/integer.c
@@ -0,0 +1,585 @@
+/**
+ * @file integer.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in integer types plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* asprintf, strdup */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesInteger (u)int(8/16/32/64) (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 1/2/4/8 | yes | pointer to the specific integer type | little-endian integer value |
+ */
+
+/**
+ * @brief LYB value size of each integer type.
+ */
+static size_t integer_lyb_size[] = {
+ [LY_TYPE_INT8] = 1, [LY_TYPE_INT16] = 2, [LY_TYPE_INT32] = 4, [LY_TYPE_INT64] = 8,
+ [LY_TYPE_UINT8] = 1, [LY_TYPE_UINT16] = 2, [LY_TYPE_UINT32] = 4, [LY_TYPE_UINT64] = 8
+};
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_int(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ int64_t num = 0;
+ int base = 1;
+ char *canon = NULL;
+ struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != integer_lyb_size[type->basetype]) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB signed integer value size %zu (expected %zu).",
+ value_len, integer_lyb_size[type->basetype]);
+ goto cleanup;
+ }
+
+ /* copy the integer and correct the byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse the integer */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ ret = lyplg_type_parse_int("int8", base, INT64_C(-128), INT64_C(127), value, value_len, &num, err);
+ break;
+ case LY_TYPE_INT16:
+ ret = lyplg_type_parse_int("int16", base, INT64_C(-32768), INT64_C(32767), value, value_len, &num, err);
+ break;
+ case LY_TYPE_INT32:
+ ret = lyplg_type_parse_int("int32", base, INT64_C(-2147483648), INT64_C(2147483647), value, value_len, &num, err);
+ break;
+ case LY_TYPE_INT64:
+ ret = lyplg_type_parse_int("int64", base, INT64_C(-9223372036854775807) - INT64_C(1),
+ INT64_C(9223372036854775807), value, value_len, &num, err);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* set the value (matters for big-endian) and get the correct int64 number */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ storage->int8 = num;
+ num = storage->int8;
+ break;
+ case LY_TYPE_INT16:
+ storage->int16 = num;
+ num = storage->int16;
+ break;
+ case LY_TYPE_INT32:
+ storage->int32 = num;
+ num = storage->int32;
+ break;
+ case LY_TYPE_INT64:
+ storage->int64 = num;
+ num = storage->int64;
+ break;
+ default:
+ break;
+ }
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* generate canonical value */
+ switch (type->basetype) {
+ case LY_TYPE_INT8:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId8, storage->int8) == -1, ret = LY_EMEM, cleanup);
+ break;
+ case LY_TYPE_INT16:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId16, storage->int16) == -1, ret = LY_EMEM, cleanup);
+ break;
+ case LY_TYPE_INT32:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId32, storage->int32) == -1, ret = LY_EMEM, cleanup);
+ break;
+ case LY_TYPE_INT64:
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRId64, storage->int64) == -1, ret = LY_EMEM, cleanup);
+ break;
+ default:
+ break;
+ }
+
+ /* store it */
+ ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* validate range of the number */
+ if (type_num->range) {
+ ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical,
+ strlen(storage->_canonical), err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_int(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ switch (val1->realtype->basetype) {
+ case LY_TYPE_INT8:
+ if (val1->int8 != val2->int8) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_INT16:
+ if (val1->int16 != val2->int16) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_INT32:
+ if (val1->int32 != val2->int32) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_INT64:
+ if (val1->int64 != val2->int64) {
+ return LY_ENOT;
+ }
+ break;
+ default:
+ break;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_int(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ int64_t prev_num = 0, num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ switch (value->realtype->basetype) {
+ case LY_TYPE_INT8:
+ prev_num = num = value->int8;
+ break;
+ case LY_TYPE_INT16:
+ prev_num = num = value->int16;
+ break;
+ case LY_TYPE_INT32:
+ prev_num = num = value->int32;
+ break;
+ case LY_TYPE_INT64:
+ prev_num = num = value->int64;
+ break;
+ default:
+ break;
+ }
+ num = htole64(num);
+ if (num == prev_num) {
+ /* values are equal, little-endian or int8 */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ return &value->int64;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, integer_lyb_size[value->realtype->basetype]);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ memcpy(buf, &num, integer_lyb_size[value->realtype->basetype]);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_uint(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *UNUSED(prefix_data), uint32_t hints,
+ const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct lys_glob_unres *UNUSED(unres),
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint64_t num = 0;
+ int base = 0;
+ char *canon;
+ struct lysc_type_num *type_num = (struct lysc_type_num *)type;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != integer_lyb_size[type->basetype]) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB unsigned integer value size %zu (expected %zu).",
+ value_len, integer_lyb_size[type->basetype]);
+ goto cleanup;
+ }
+
+ /* copy the integer and correct the byte order */
+ memcpy(&num, value, value_len);
+ num = le64toh(num);
+ } else {
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, &base, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* parse the integer */
+ switch (type->basetype) {
+ case LY_TYPE_UINT8:
+ ret = lyplg_type_parse_uint("uint8", base, UINT64_C(255), value, value_len, &num, err);
+ break;
+ case LY_TYPE_UINT16:
+ ret = lyplg_type_parse_uint("uint16", base, UINT64_C(65535), value, value_len, &num, err);
+ break;
+ case LY_TYPE_UINT32:
+ ret = lyplg_type_parse_uint("uint32", base, UINT64_C(4294967295), value, value_len, &num, err);
+ break;
+ case LY_TYPE_UINT64:
+ ret = lyplg_type_parse_uint("uint64", base, UINT64_C(18446744073709551615), value, value_len, &num, err);
+ break;
+ default:
+ LOGINT(ctx);
+ ret = LY_EINT;
+ }
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* store value, matters for big-endian */
+ switch (type->basetype) {
+ case LY_TYPE_UINT8:
+ storage->uint8 = num;
+ break;
+ case LY_TYPE_UINT16:
+ storage->uint16 = num;
+ break;
+ case LY_TYPE_UINT32:
+ storage->uint32 = num;
+ break;
+ case LY_TYPE_UINT64:
+ storage->uint64 = num;
+ break;
+ default:
+ break;
+ }
+
+ if (format == LY_VALUE_CANON) {
+ /* store canonical value */
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ ret = lydict_insert_zc(ctx, (char *)value, &storage->_canonical);
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ ret = lydict_insert(ctx, value, value_len, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ } else {
+ /* generate canonical value */
+ LY_CHECK_ERR_GOTO(asprintf(&canon, "%" PRIu64, num) == -1, ret = LY_EMEM, cleanup);
+
+ /* store it */
+ ret = lydict_insert_zc(ctx, canon, (const char **)&storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ /* validate range of the number */
+ if (type_num->range) {
+ ret = lyplg_type_validate_range(type->basetype, type_num->range, num, storage->_canonical,
+ strlen(storage->_canonical), err);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_simple(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_uint(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ switch (val1->realtype->basetype) {
+ case LY_TYPE_UINT8:
+ if (val1->uint8 != val2->uint8) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_UINT16:
+ if (val1->uint16 != val2->uint16) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_UINT32:
+ if (val1->uint32 != val2->uint32) {
+ return LY_ENOT;
+ }
+ break;
+ case LY_TYPE_UINT64:
+ if (val1->uint64 != val2->uint64) {
+ return LY_ENOT;
+ }
+ break;
+ default:
+ break;
+ }
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_uint(const struct ly_ctx *UNUSED(ctx), const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ uint64_t num = 0;
+ void *buf;
+
+ if (format == LY_VALUE_LYB) {
+ switch (value->realtype->basetype) {
+ case LY_TYPE_UINT8:
+ num = value->uint8;
+ break;
+ case LY_TYPE_UINT16:
+ num = value->uint16;
+ break;
+ case LY_TYPE_UINT32:
+ num = value->uint32;
+ break;
+ case LY_TYPE_UINT64:
+ num = value->uint64;
+ break;
+ default:
+ break;
+ }
+ num = htole64(num);
+ if (num == value->uint64) {
+ /* values are equal, little-endian or uint8 */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ return &value->uint64;
+ } else {
+ /* values differ, big-endian */
+ buf = calloc(1, integer_lyb_size[value->realtype->basetype]);
+ LY_CHECK_RET(!buf, NULL);
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = integer_lyb_size[value->realtype->basetype];
+ }
+ memcpy(buf, &num, integer_lyb_size[value->realtype->basetype]);
+ return buf;
+ }
+ }
+
+ /* use the cached canonical value */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+}
+
+/**
+ * @brief Plugin information for integer types implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_integer[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT8_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 1,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT16_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 2,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT32_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UINT64_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_uint,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_uint,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_uint,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 8,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT8_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 1,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT16_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 2,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT32_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ }, {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_INT64_STR,
+
+ .plugin.id = "libyang 2 - integers, version 1",
+ .plugin.store = lyplg_type_store_int,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_int,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_int,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 8,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_address.c b/src/plugins_types/ipv4_address.c
new file mode 100644
index 0000000..f7b297c
--- /dev/null
+++ b/src/plugins_types/ipv4_address.c
@@ -0,0 +1,377 @@
+/**
+ * @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 /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#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 |
+ */
+
+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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 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;
+ }
+ }
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+
+ /* store zone, if any */
+ if (value_len > 4) {
+ ret = lydict_insert(ctx, value_str + 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);
+
+ /* 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, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ /* 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;
+ size_t zone_len;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ 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_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, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+ ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
+ LY_CHECK_GOTO(ret, error);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv4_address(ctx, dup);
+ return ret;
+}
+
+/**
+ * @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;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ lydict_remove(ctx, val->zone);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(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.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv4_address,
+ .plugin.duplicate = lyplg_type_dup_ipv4_address,
+ .plugin.free = lyplg_type_free_ipv4_address,
+ .plugin.lyb_data_len = -1,
+ },
+ {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..91fe677
--- /dev/null
+++ b/src/plugins_types/ipv4_address_no_zone.c
@@ -0,0 +1,221 @@
+/**
+ * @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 /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#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 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;
+ struct lyd_value_ipv4_address_no_zone *val;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != 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;
+ }
+
+ /* store IP address */
+ memcpy(&val->addr, 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);
+
+ /* 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, &val->addr)) {
+ 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)
+{
+ struct lyd_value_ipv4_address_no_zone *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ if (memcmp(&v1->addr, &v2->addr, sizeof v1->addr)) {
+ 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)
+{
+ struct lyd_value_ipv4_address_no_zone *val;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = 4;
+ }
+ return &val->addr;
+ }
+
+ /* 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, &val->addr, 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 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.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv4_address_no_zone,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = 4,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv4_prefix.c b/src/plugins_types/ipv4_prefix.c
new file mode 100644
index 0000000..6f13eee
--- /dev/null
+++ b/src/plugins_types/ipv4_prefix.c
@@ -0,0 +1,337 @@
+/**
+ * @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 /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <stdint.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 |
+ */
+
+#define LYB_VALUE_LEN 5
+
+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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != LYB_VALUE_LEN) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv4-prefix value size %zu (expected %d).",
+ value_len, LYB_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 addr + prefix */
+ memcpy(val, value, value_len);
+
+ /* 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);
+
+ /* 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, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ 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;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = LYB_VALUE_LEN;
+ }
+ 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_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, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(dup_val, orig_val, sizeof *orig_val);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv4_prefix(ctx, dup);
+ return ret;
+}
+
+/**
+ * @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)
+{
+ struct lyd_value_ipv4_prefix *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+}
+
+/**
+ * @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.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv4_prefix,
+ .plugin.duplicate = lyplg_type_dup_ipv4_prefix,
+ .plugin.free = lyplg_type_free_ipv4_prefix,
+ .plugin.lyb_data_len = LYB_VALUE_LEN,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_address.c b/src/plugins_types/ipv6_address.c
new file mode 100644
index 0000000..74f5c62
--- /dev/null
+++ b/src/plugins_types/ipv6_address.c
@@ -0,0 +1,378 @@
+/**
+ * @file ipv6_address.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-inet-types ipv6-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 /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#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 howtoDataLYBTypesIPv6Address ipv6-address (ietf-inet-types)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 16 | yes | `struct in6_addr *` | IPv6 address in network-byte order |
+ * | string length | no | `char *` | IPv6 address zone string |
+ */
+
+static void lyplg_type_free_ipv6_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
+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 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,
+ 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_ipv6_address *val;
+ size_t i;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len < 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;
+ }
+ }
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+
+ /* store zone, if any */
+ if (value_len > 16) {
+ ret = lydict_insert(ctx, value_str + 16, value_len - 16, &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);
+
+ /* 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 */
+ 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(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @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)
+{
+ struct lyd_value_ipv6_address *v1, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ /* 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 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,
+ void *UNUSED(prefix_data), ly_bool *dynamic, size_t *value_len)
+{
+ struct lyd_value_ipv6_address *val;
+ size_t zone_len;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ 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(INET6_ADDRSTRLEN + zone_len);
+ 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;
+ }
+
+ /* 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_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)
+{
+ LY_ERR ret;
+ struct lyd_value_ipv6_address *orig_val, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+ ret = lydict_insert(ctx, orig_val->zone, 0, &dup_val->zone);
+ LY_CHECK_GOTO(ret, error);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv6_address(ctx, dup);
+ return ret;
+}
+
+/**
+ * @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;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ lydict_remove(ctx, val->zone);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(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_ipv6_address[] = {
+ {
+ .module = "ietf-inet-types",
+ .revision = "2013-07-15",
+ .name = "ipv6-address",
+
+ .plugin.id = "libyang 2 - ipv6-address, version 1",
+ .plugin.store = lyplg_type_store_ipv6_address,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_ipv6_address,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv6_address,
+ .plugin.duplicate = lyplg_type_dup_ipv6_address,
+ .plugin.free = lyplg_type_free_ipv6_address,
+ .plugin.lyb_data_len = -1,
+ },
+ {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..26fbf80
--- /dev/null
+++ b/src/plugins_types/ipv6_address_no_zone.c
@@ -0,0 +1,312 @@
+/**
+ * @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 /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#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 |
+ */
+
+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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ 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;
+ }
+
+ if ((options & LYPLG_TYPE_STORE_DYNAMIC) && LYPLG_TYPE_VAL_IS_DYN(val)) {
+ /* use the value directly */
+ storage->dyn_mem = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else {
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ /* store IP address */
+ memcpy(&val->addr, value, sizeof val->addr);
+ }
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, 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);
+
+ /* 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, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ 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;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ 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_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, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(&dup_val->addr, &orig_val->addr, sizeof orig_val->addr);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv6_address_no_zone(ctx, dup);
+ return ret;
+}
+
+/**
+ * @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_no_zone *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(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.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv6_address_no_zone,
+ .plugin.duplicate = lyplg_type_dup_ipv6_address_no_zone,
+ .plugin.free = lyplg_type_free_ipv6_address_no_zone,
+ .plugin.lyb_data_len = 16,
+ },
+ {0}
+};
diff --git a/src/plugins_types/ipv6_prefix.c b/src/plugins_types/ipv6_prefix.c
new file mode 100644
index 0000000..8e62311
--- /dev/null
+++ b/src/plugins_types/ipv6_prefix.c
@@ -0,0 +1,351 @@
+/**
+ * @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 /* strndup */
+
+#include "plugins_types.h"
+
+#ifdef _WIN32
+# include <winsock2.h>
+# include <ws2tcpip.h>
+#else
+# include <arpa/inet.h>
+# if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__)
+# include <netinet/in.h>
+# include <sys/socket.h>
+# endif
+#endif
+#include <stdint.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 |
+ */
+
+#define LYB_VALUE_LEN 17
+
+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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ if (format == LY_VALUE_LYB) {
+ /* validation */
+ if (value_len != LYB_VALUE_LEN) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB ipv6-prefix value size %zu (expected %d).",
+ value_len, LYB_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) && LYPLG_TYPE_VAL_IS_DYN(val)) {
+ storage->dyn_mem = (void *)value;
+ options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+
+ LYD_VALUE_GET(storage, val);
+ } else {
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+
+ memcpy(val, value, value_len);
+ }
+
+ /* zero host */
+ ipv6prefix_zero_host(&val->addr, val->prefix);
+
+ /* success */
+ goto cleanup;
+ }
+
+ /* allocate value */
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, 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);
+
+ /* 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, *v2;
+
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ LYD_VALUE_GET(val1, v1);
+ LYD_VALUE_GET(val2, v2);
+
+ 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;
+ char *ret;
+
+ LYD_VALUE_GET(value, val);
+
+ if (format == LY_VALUE_LYB) {
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = LYB_VALUE_LEN;
+ }
+ 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_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, *dup_val;
+
+ memset(dup, 0, sizeof *dup);
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, error);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, ret = LY_EMEM, error);
+
+ LYD_VALUE_GET(original, orig_val);
+ memcpy(dup_val, orig_val, sizeof *orig_val);
+
+ dup->realtype = original->realtype;
+ return LY_SUCCESS;
+
+error:
+ lyplg_type_free_ipv6_prefix(ctx, dup);
+ return ret;
+}
+
+/**
+ * @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)
+{
+ struct lyd_value_ipv6_prefix *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+}
+
+/**
+ * @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.sort = NULL,
+ .plugin.print = lyplg_type_print_ipv6_prefix,
+ .plugin.duplicate = lyplg_type_dup_ipv6_prefix,
+ .plugin.free = lyplg_type_free_ipv6_prefix,
+ .plugin.lyb_data_len = LYB_VALUE_LEN,
+ },
+ {0}
+};
diff --git a/src/plugins_types/leafref.c b/src/plugins_types/leafref.c
new file mode 100644
index 0000000..8ab3fc5
--- /dev/null
+++ b/src/plugins_types/leafref.c
@@ -0,0 +1,140 @@
+/**
+ * @file leafref.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in leafref type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesLeafref leafref (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------: | :-------: | :--: | :-----: |
+ * | exact same format as the leafref target ||||
+ */
+
+LIBYANG_API_DEF 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)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
+
+ assert(type_lr->realtype);
+
+ /* store the value as the real type of the leafref target */
+ ret = type_lr->realtype->plugin->store(ctx, type_lr->realtype, value, value_len, options, format, prefix_data,
+ hints, ctx_node, storage, unres, err);
+ if (ret == LY_EINCOMPLETE) {
+ /* it is irrelevant whether the target type needs some resolving */
+ ret = LY_SUCCESS;
+ }
+ LY_CHECK_RET(ret);
+
+ if (type_lr->require_instance) {
+ /* needs to be resolved */
+ return LY_EINCOMPLETE;
+ } else {
+ return LY_SUCCESS;
+ }
+}
+
+LIBYANG_API_DEF LY_ERR
+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;
+ struct lysc_type_leafref *type_lr = (struct lysc_type_leafref *)type;
+ char *errmsg = NULL, *path;
+
+ *err = NULL;
+
+ if (!type_lr->require_instance) {
+ /* redundant to resolve */
+ return LY_SUCCESS;
+ }
+
+ /* check leafref target existence */
+ if (lyplg_type_resolve_leafref(type_lr, ctx_node, storage, tree, NULL, &errmsg)) {
+ path = lyd_path(ctx_node, LYD_PATH_STD, NULL, 0);
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, path, strdup("instance-required"), "%s", errmsg);
+ free(errmsg);
+ return ret;
+ }
+
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_leafref(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ return val1->realtype->plugin->compare(val1, val2);
+}
+
+LIBYANG_API_DEF 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)
+{
+ return value->realtype->plugin->print(ctx, value, format, prefix_data, dynamic, value_len);
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_leafref(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ return original->realtype->plugin->duplicate(ctx, original, dup);
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_leafref(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ value->realtype->plugin->free(ctx, value);
+}
+
+/**
+ * @brief Plugin information for leafref type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_leafref[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_LEAFREF_STR,
+
+ .plugin.id = "libyang 2 - leafref, version 1",
+ .plugin.store = lyplg_type_store_leafref,
+ .plugin.validate = lyplg_type_validate_leafref,
+ .plugin.compare = lyplg_type_compare_leafref,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_leafref,
+ .plugin.duplicate = lyplg_type_dup_leafref,
+ .plugin.free = lyplg_type_free_leafref,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c
new file mode 100644
index 0000000..04fb164
--- /dev/null
+++ b/src/plugins_types/node_instanceid.c
@@ -0,0 +1,320 @@
+/**
+ * @file node_instanceid.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-netconf-acm node-instance-identifier 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
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "path.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+#include "xpath.h"
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesNodeInstanceIdentifier node-instance-identifier (ietf-netconf-acm)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string JSON format of the instance-identifier |
+ */
+
+/**
+ * @brief Convert compiled path (node-instance-identifier) or NULL ("/") 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
+node_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, quot;
+ const struct lys_module *mod = NULL;
+ ly_bool inherit_prefix = 0, d;
+ const char *strval;
+
+ if (!path) {
+ /* special path */
+ ret = ly_strcat(&result, "/");
+ goto cleanup;
+ }
+
+ switch (format) {
+ case LY_VALUE_XML:
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ /* everything is prefixed */
+ inherit_prefix = 0;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* the same prefix is inherited and skipped */
+ inherit_prefix = 1;
+ 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);
+
+ /* 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 {
+ 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);
+ }
+ 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);
+
+ /* default quote */
+ quot = '\'';
+ if (strchr(strval, quot)) {
+ quot = '"';
+ }
+ ret = ly_strcat(&result, "[.=%c%s%c]", quot, strval, quot);
+ if (d) {
+ free((char *)strval);
+ }
+ break;
+ }
+
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ if (ret) {
+ free(result);
+ } else {
+ *str = result;
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_store_clb for the node-instance-identifier ietf-netconf-acm type.
+ */
+static LY_ERR
+lyplg_type_store_node_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)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyxp_expr *exp = NULL;
+ uint32_t prefix_opt = 0;
+ struct ly_path *path = NULL;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* check hints */
+ ret = lyplg_type_check_hints(hints, value, value_len, type->basetype, NULL, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if ((((char *)value)[0] == '/') && (value_len == 1)) {
+ /* special path */
+ goto store;
+ }
+
+ switch (format) {
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ prefix_opt = LY_PATH_PREFIX_MANDATORY;
+ break;
+ case LY_VALUE_CANON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_JSON:
+ case LY_VALUE_STR_NS:
+ prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT;
+ break;
+ }
+
+ /* parse the value */
+ ret = ly_path_parse(ctx, ctx_node, value, value_len, 0, LY_PATH_BEGIN_ABSOLUTE, prefix_opt, LY_PATH_PRED_SIMPLE, &exp);
+ if (ret) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - syntax error.", (int)value_len, (char *)value);
+ goto cleanup;
+ }
+
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ /* implement all prefixes */
+ LY_CHECK_GOTO(ret = lys_compile_expr_implement(ctx, exp, format, prefix_data, 1, unres, NULL), cleanup);
+ }
+
+ /* resolve it on schema tree, use JSON format instead of LYB because for this type they are equal but for some
+ * nested types (such as numbers in predicates in the path) LYB would be invalid */
+ ret = ly_path_compile(ctx, NULL, ctx_node, NULL, exp, (ctx_node && (ctx_node->flags & LYS_IS_OUTPUT)) ?
+ LY_PATH_OPER_OUTPUT : LY_PATH_OPER_INPUT, LY_PATH_TARGET_MANY, 1, (format == LY_VALUE_LYB) ?
+ LY_VALUE_JSON : format, prefix_data, &path);
+ if (ret) {
+ ret = ly_err_new(err, ret, LYVE_DATA, NULL, NULL,
+ "Invalid instance-identifier \"%.*s\" value - semantic error.", (int)value_len, (char *)value);
+ goto cleanup;
+ }
+
+store:
+ /* store value */
+ storage->target = path;
+
+ /* 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 = node_instanceid_path2str(path, LY_VALUE_JSON, NULL, &canon);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+cleanup:
+ lyxp_expr_free(ctx, exp);
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_instanceid(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_print_clb for the node-instance-identifier ietf-netconf-acm type.
+ */
+static const void *
+lyplg_type_print_node_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 (node_instanceid_path2str(value->target, format, prefix_data, &ret)) {
+ return NULL;
+ }
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for instance-identifier type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_node_instanceid[] = {
+ {
+ .module = "ietf-netconf-acm",
+ .revision = "2012-02-22",
+ .name = "node-instance-identifier",
+
+ .plugin.id = "libyang 2 - node-instance-identifier, version 1",
+ .plugin.store = lyplg_type_store_node_instanceid,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_instanceid,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_node_instanceid,
+ .plugin.duplicate = lyplg_type_dup_instanceid,
+ .plugin.free = lyplg_type_free_instanceid,
+ .plugin.lyb_data_len = -1,
+ },
+ {
+ .module = "ietf-netconf-acm",
+ .revision = "2018-02-14",
+ .name = "node-instance-identifier",
+
+ .plugin.id = "libyang 2 - node-instance-identifier, version 1",
+ .plugin.store = lyplg_type_store_node_instanceid,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_instanceid,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_node_instanceid,
+ .plugin.duplicate = lyplg_type_dup_instanceid,
+ .plugin.free = lyplg_type_free_instanceid,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/string.c b/src/plugins_types/string.c
new file mode 100644
index 0000000..4f988ef
--- /dev/null
+++ b/src/plugins_types/string.c
@@ -0,0 +1,109 @@
+/**
+ * @file string.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in string 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
+ */
+
+#include "plugins_types.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesString string (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | string length | yes | `char *` | string itself |
+ */
+
+LIBYANG_API_DEF 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,
+ 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;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ storage->realtype = type;
+
+ /* 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);
+
+ /* 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_simple(ctx, storage);
+ }
+ return ret;
+}
+
+/**
+ * @brief Plugin information for string type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_string[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_STRING_STR,
+
+ .plugin.id = "libyang 2 - string, version 1",
+ .plugin.store = lyplg_type_store_string,
+ .plugin.validate = NULL,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_simple,
+ .plugin.duplicate = lyplg_type_dup_simple,
+ .plugin.free = lyplg_type_free_simple,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/union.c b/src/plugins_types/union.c
new file mode 100644
index 0000000..6e31d1e
--- /dev/null
+++ b/src/plugins_types/union.c
@@ -0,0 +1,585 @@
+/**
+ * @file union.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Built-in union type plugin.
+ *
+ * Copyright (c) 2019-2021 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE /* strdup */
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+/* additional internal headers for some useful simple macros */
+#include "common.h"
+#include "compat.h"
+#include "plugins_internal.h" /* LY_TYPE_*_STR */
+
+/**
+ * @page howtoDataLYB LYB Binary Format
+ * @subsection howtoDataLYBTypesUnion union (built-in)
+ *
+ * | Size (B) | Mandatory | Type | Meaning |
+ * | :------ | :-------: | :--: | :-----: |
+ * | 4 | yes | `uint32_t *` | little-endian index of the resolved type in ::lysc_type_union.types |
+ * | exact same format as the resolved type ||||
+ *
+ * Note that loading union value in this format prevents it from changing its real (resolved) type.
+ */
+
+/**
+ * @brief Size in bytes of the index in the LYB Binary Format.
+ */
+#define IDX_SIZE 4
+
+/**
+ * @brief Assign a value to the union subvalue.
+ *
+ * @param[in] value Value for assignment.
+ * @param[in] value_len Length of the @p value.
+ * @param[out] original Destination item of the subvalue.
+ * @param[out] orig_len Length of the @p original.
+ * @param[in,out] options Flag containing LYPLG_TYPE_STORE_DYNAMIC.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_subvalue_assignment(const void *value, size_t value_len, void **original, size_t *orig_len, uint32_t *options)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (*options & LYPLG_TYPE_STORE_DYNAMIC) {
+ /* The allocated value is stored and spend. */
+ *original = (void *)value;
+ *options &= ~LYPLG_TYPE_STORE_DYNAMIC;
+ } else if (value_len) {
+ /* Make copy of the value. */
+ *original = calloc(1, value_len);
+ LY_CHECK_ERR_RET(!*original, ret = LY_EMEM, ret);
+ memcpy(*original, value, value_len);
+ } else {
+ /* Empty value. */
+ *original = strdup("");
+ LY_CHECK_ERR_RET(!*original, ret = LY_EMEM, ret);
+ }
+ *orig_len = value_len;
+
+ return ret;
+}
+
+/**
+ * @brief Validate LYB Binary Format.
+ *
+ * @param[in] lyb_data Source of LYB data to parse.
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[in] type_u Compiled type of union.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_union_validate(const void *lyb_data, size_t lyb_data_len, const struct lysc_type_union *type_u, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint64_t type_idx = 0;
+
+ /* Basic validation. */
+ if (lyb_data_len < IDX_SIZE) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid LYB union value size %zu (expected at least 4).",
+ lyb_data_len);
+ return ret;
+ }
+
+ /* Get index in correct byte order. */
+ memcpy(&type_idx, lyb_data, IDX_SIZE);
+ type_idx = le64toh(type_idx);
+ if (type_idx >= LY_ARRAY_COUNT(type_u->types)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid LYB union type index %" PRIu64 " (type count %" LY_PRI_ARRAY_COUNT_TYPE ").",
+ type_idx, LY_ARRAY_COUNT(type_u->types));
+ return ret;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Parse index and lyb_value from LYB Binary Format.
+ *
+ * @param[in] lyb_data Source of LYB data to parse.
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[out] type_idx Index of the union type.
+ * @param[out] lyb_value Value after index number. If there is no value
+ * after the index, it is set to empty string ("").
+ * @param[out] lyb_value_len Length of @p lyb_value.
+ */
+static void
+lyb_parse_union(const void *lyb_data, size_t lyb_data_len, uint32_t *type_idx, const void **lyb_value, size_t *lyb_value_len)
+{
+ uint64_t num = 0;
+
+ assert(lyb_data && !(lyb_value && !lyb_value_len));
+
+ if (type_idx) {
+ memcpy(&num, lyb_data, IDX_SIZE);
+ num = le64toh(num);
+
+ *type_idx = num;
+ }
+
+ if (lyb_value && lyb_value_len && lyb_data_len) {
+ /* Get lyb_value and its length. */
+ if (lyb_data_len == IDX_SIZE) {
+ *lyb_value_len = 0;
+ *lyb_value = "";
+ } else {
+ *lyb_value_len = lyb_data_len - IDX_SIZE;
+ *lyb_value = (char *)lyb_data + IDX_SIZE;
+ }
+ }
+}
+
+/**
+ * @brief Store subvalue as a specific type.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type Specific union type to use for storing.
+ * @param[in] subvalue Union subvalue structure.
+ * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] ctx_node Context node for prefix resolution.
+ * @param[in] tree Data tree for resolving (validation).
+ * @param[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_store_type(const struct ly_ctx *ctx, struct lysc_type *type, struct lyd_value_union *subvalue,
+ ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, struct lys_glob_unres *unres,
+ struct ly_err_item **err)
+{
+ LY_ERR ret;
+ const void *value = NULL;
+ size_t value_len = 0;
+
+ if (subvalue->format == LY_VALUE_LYB) {
+ lyb_parse_union(subvalue->original, subvalue->orig_len, NULL, &value, &value_len);
+ } else {
+ value = subvalue->original;
+ value_len = subvalue->orig_len;
+ }
+
+ ret = type->plugin->store(ctx, type, value, value_len, 0, subvalue->format, subvalue->prefix_data, subvalue->hints,
+ subvalue->ctx_node, &subvalue->value, unres, err);
+ if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+ /* clear any leftover/freed garbage */
+ memset(&subvalue->value, 0, sizeof subvalue->value);
+ return ret;
+ }
+
+ if (resolve && (ret == LY_EINCOMPLETE)) {
+ /* we need the value resolved */
+ ret = type->plugin->validate(ctx, type, ctx_node, tree, &subvalue->value, err);
+ if (ret) {
+ /* resolve failed, we need to free the stored value */
+ type->plugin->free(ctx, &subvalue->value);
+ }
+ }
+
+ return ret;
+}
+
+/**
+ * @brief Find the first valid type for a union value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] types Sized array of union types.
+ * @param[in] subvalue Union subvalue structure.
+ * @param[in] resolve Whether the value needs to be resolved (validated by a callback).
+ * @param[in] ctx_node Context node for prefix resolution.
+ * @param[in] tree Data tree for resolving (validation).
+ * @param[out] type_idx Index of the type in which the value was stored.
+ * @param[in,out] unres Global unres structure.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+union_find_type(const struct ly_ctx *ctx, struct lysc_type **types, struct lyd_value_union *subvalue,
+ ly_bool resolve, const struct lyd_node *ctx_node, const struct lyd_node *tree, uint32_t *type_idx,
+ struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t temp_lo = 0;
+
+ if (!types || !LY_ARRAY_COUNT(types)) {
+ return LY_EINVAL;
+ }
+
+ *err = NULL;
+
+ /* turn logging temporarily off */
+ ly_temp_log_options(&temp_lo);
+
+ /* use the first usable subtype to store the value */
+ for (u = 0; u < LY_ARRAY_COUNT(types); ++u) {
+ ret = union_store_type(ctx, types[u], subvalue, resolve, ctx_node, tree, unres, err);
+ if ((ret == LY_SUCCESS) || (ret == LY_EINCOMPLETE)) {
+ break;
+ }
+
+ ly_err_free(*err);
+ *err = NULL;
+ }
+
+ if (u == LY_ARRAY_COUNT(types)) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Invalid union value \"%.*s\" - no matching subtype found.",
+ (int)subvalue->orig_len, (char *)subvalue->original);
+ } else if (type_idx) {
+ *type_idx = u;
+ }
+
+ /* restore logging */
+ ly_temp_log_options(NULL);
+ return ret;
+}
+
+/**
+ * @brief Fill union subvalue items: original, origin_len, format prefix_data and call 'store' function for value.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type_u Compiled type of union.
+ * @param[in] lyb_data Input LYB data consisting of index followed by value (lyb_value).
+ * @param[in] lyb_data_len Length of @p lyb_data.
+ * @param[in] prefix_data Format-specific data for resolving any prefixes (see ly_resolve_prefix()).
+ * @param[in,out] subvalue Union subvalue to be filled.
+ * @param[in,out] options Option containing LYPLG_TYPE_STORE_DYNAMIC.
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyb_fill_subvalue(const struct ly_ctx *ctx, struct lysc_type_union *type_u, const void *lyb_data, size_t lyb_data_len,
+ void *prefix_data, struct lyd_value_union *subvalue, uint32_t *options, struct lys_glob_unres *unres,
+ struct ly_err_item **err)
+{
+ LY_ERR ret;
+ uint32_t type_idx;
+ const void *lyb_value = NULL;
+ size_t lyb_value_len = 0;
+
+ ret = lyb_union_validate(lyb_data, lyb_data_len, type_u, err);
+ LY_CHECK_RET(ret);
+
+ /* Parse lyb_data and set the lyb_value and lyb_value_len. */
+ lyb_parse_union(lyb_data, lyb_data_len, &type_idx, &lyb_value, &lyb_value_len);
+ LY_CHECK_RET(ret);
+
+ /* Store lyb_data to subvalue. */
+ ret = union_subvalue_assignment(lyb_data, lyb_data_len,
+ &subvalue->original, &subvalue->orig_len, options);
+ LY_CHECK_RET(ret);
+
+ if (lyb_value) {
+ /* Resolve prefix_data and set format. */
+ ret = lyplg_type_prefix_data_new(ctx, lyb_value, lyb_value_len, LY_VALUE_LYB, prefix_data, &subvalue->format,
+ &subvalue->prefix_data);
+ LY_CHECK_RET(ret);
+ assert(subvalue->format == LY_VALUE_LYB);
+ } else {
+ /* The lyb_parse_union() did not find lyb_value.
+ * Just set format.
+ */
+ subvalue->format = LY_VALUE_LYB;
+ }
+
+ /* Use the specific type to store the value. */
+ ret = union_store_type(ctx, type_u->types[type_idx], subvalue, 0, NULL, NULL, unres, err);
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_store_union(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
+ uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
+ struct lyd_value *storage, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS, r;
+ struct lysc_type_union *type_u = (struct lysc_type_union *)type;
+ struct lyd_value_union *subvalue;
+
+ *err = NULL;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, subvalue);
+ LY_CHECK_ERR_GOTO(!subvalue, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+ subvalue->hints = hints;
+ subvalue->ctx_node = ctx_node;
+
+ if (format == LY_VALUE_LYB) {
+ ret = lyb_fill_subvalue(ctx, type_u, value, value_len,
+ prefix_data, subvalue, &options, unres, err);
+ LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ } else {
+ /* Store @p value to subvalue. */
+ ret = union_subvalue_assignment(value, value_len,
+ &subvalue->original, &subvalue->orig_len, &options);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* store format-specific data for later prefix resolution */
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &subvalue->format,
+ &subvalue->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, NULL, unres, err);
+ LY_CHECK_GOTO((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE), cleanup);
+ }
+
+ /* store canonical value, if any (use the specific type value) */
+ r = lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical);
+ LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if ((ret != LY_SUCCESS) && (ret != LY_EINCOMPLETE)) {
+ lyplg_type_free_union(ctx, storage);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_validate_union(const struct ly_ctx *ctx, const struct lysc_type *type, const struct lyd_node *ctx_node,
+ const struct lyd_node *tree, struct lyd_value *storage, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lysc_type_union *type_u = (struct lysc_type_union *)type;
+ struct lyd_value_union *subvalue = storage->subvalue;
+
+ *err = NULL;
+
+ /* because of types that do not store their own type as realtype (leafref), we are not able to call their
+ * validate callback (there is no way to get the type TODO could be added to struct lyd_value_union), so
+ * we have to perform union value storing again from scratch */
+ subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
+
+ /* use the first usable subtype to store the value */
+ ret = union_find_type(ctx, type_u->types, subvalue, 1, ctx_node, tree, NULL, NULL, err);
+ LY_CHECK_RET(ret);
+
+ /* success, update the canonical value, if any generated */
+ lydict_remove(ctx, storage->_canonical);
+ LY_CHECK_RET(lydict_insert(ctx, subvalue->value._canonical, 0, &storage->_canonical));
+ return LY_SUCCESS;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_compare_union(const struct lyd_value *val1, const struct lyd_value *val2)
+{
+ if (val1->realtype != val2->realtype) {
+ return LY_ENOT;
+ }
+
+ if (val1->subvalue->value.realtype != val2->subvalue->value.realtype) {
+ return LY_ENOT;
+ }
+ return val1->subvalue->value.realtype->plugin->compare(&val1->subvalue->value, &val2->subvalue->value);
+}
+
+/**
+ * @brief Create LYB data for printing.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] type_u Compiled type of union.
+ * @param[in] subvalue Union value.
+ * @param[in] prefix_data Format-specific data for resolving any
+ * prefixes (see ly_resolve_prefix()).
+ * @param[out] value_len Length of returned data.
+ * @return Pointer to created LYB data. Caller must release.
+ * @return NULL in case of error.
+ */
+static const void *
+lyb_union_print(const struct ly_ctx *ctx, struct lysc_type_union *type_u, struct lyd_value_union *subvalue,
+ void *prefix_data, size_t *value_len)
+{
+ void *ret = NULL;
+ LY_ERR retval;
+ struct ly_err_item *err;
+ uint64_t num = 0;
+ uint32_t type_idx;
+ ly_bool dynamic;
+ size_t pval_len;
+ void *pval;
+
+ /* Find out the index number (type_idx). The call should succeed
+ * because the union_find_type() has already been called in the
+ * lyplg_type_store_union().
+ */
+ if (!ctx) {
+ assert(subvalue->ctx_node);
+ ctx = subvalue->ctx_node->module->ctx;
+ }
+ subvalue->value.realtype->plugin->free(ctx, &subvalue->value);
+ retval = union_find_type(ctx, type_u->types, subvalue, 0, NULL, NULL, &type_idx, NULL, &err);
+ LY_CHECK_RET((retval != LY_SUCCESS) && (retval != LY_EINCOMPLETE), NULL);
+
+ /* Print subvalue in LYB format. */
+ pval = (void *)subvalue->value.realtype->plugin->print(NULL, &subvalue->value, LY_VALUE_LYB, prefix_data, &dynamic,
+ &pval_len);
+ LY_CHECK_RET(!pval, NULL);
+
+ /* Create LYB data. */
+ *value_len = IDX_SIZE + pval_len;
+ ret = malloc(*value_len);
+ LY_CHECK_RET(!ret, NULL);
+
+ num = type_idx;
+ num = htole64(num);
+ memcpy(ret, &num, IDX_SIZE);
+ memcpy((char *)ret + IDX_SIZE, pval, pval_len);
+
+ if (dynamic) {
+ free(pval);
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF const void *
+lyplg_type_print_union(const struct ly_ctx *ctx, const struct lyd_value *value, LY_VALUE_FORMAT format,
+ void *prefix_data, ly_bool *dynamic, size_t *value_len)
+{
+ const void *ret;
+ struct lyd_value_union *subvalue = value->subvalue;
+ struct lysc_type_union *type_u = (struct lysc_type_union *)value->realtype;
+ size_t lyb_data_len = 0;
+
+ if ((format == LY_VALUE_LYB) && (subvalue->format == LY_VALUE_LYB)) {
+ /* The return value is already ready. */
+ *dynamic = 0;
+ if (value_len) {
+ *value_len = subvalue->orig_len;
+ }
+ return subvalue->original;
+ } else if ((format == LY_VALUE_LYB) && (subvalue->format != LY_VALUE_LYB)) {
+ /* The return LYB data must be created. */
+ *dynamic = 1;
+ ret = lyb_union_print(ctx, type_u, subvalue, prefix_data, &lyb_data_len);
+ if (value_len) {
+ *value_len = lyb_data_len;
+ }
+ return ret;
+ }
+
+ assert(format != LY_VALUE_LYB);
+ ret = (void *)subvalue->value.realtype->plugin->print(ctx, &subvalue->value, format, prefix_data, dynamic, value_len);
+ if (!value->_canonical && (format == LY_VALUE_CANON)) {
+ /* the canonical value is supposed to be stored now */
+ lydict_insert(ctx, subvalue->value._canonical, 0, (const char **)&value->_canonical);
+ }
+
+ return ret;
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_dup_union(const struct ly_ctx *ctx, const struct lyd_value *original, struct lyd_value *dup)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_value_union *orig_val = original->subvalue, *dup_val;
+
+ /* init dup value */
+ memset(dup, 0, sizeof *dup);
+ dup->realtype = original->realtype;
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ dup_val = calloc(1, sizeof *dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ dup->subvalue = dup_val;
+
+ ret = orig_val->value.realtype->plugin->duplicate(ctx, &orig_val->value, &dup_val->value);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ if (orig_val->orig_len) {
+ dup_val->original = calloc(1, orig_val->orig_len);
+ LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ memcpy(dup_val->original, orig_val->original, orig_val->orig_len);
+ } else {
+ dup_val->original = strdup("");
+ LY_CHECK_ERR_GOTO(!dup_val->original, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ }
+ dup_val->orig_len = orig_val->orig_len;
+
+ dup_val->format = orig_val->format;
+ dup_val->ctx_node = orig_val->ctx_node;
+ dup_val->hints = orig_val->hints;
+ ret = lyplg_type_prefix_data_dup(ctx, orig_val->format, orig_val->prefix_data, &dup_val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+
+cleanup:
+ if (ret) {
+ lyplg_type_free_union(ctx, dup);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_union(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_union *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ if (val->value.realtype) {
+ val->value.realtype->plugin->free(ctx, &val->value);
+ }
+ lyplg_type_prefix_data_free(val->format, val->prefix_data);
+ free(val->original);
+
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for union type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_union[] = {
+ {
+ .module = "",
+ .revision = NULL,
+ .name = LY_TYPE_UNION_STR,
+
+ .plugin.id = "libyang 2 - union,version 1",
+ .plugin.store = lyplg_type_store_union,
+ .plugin.validate = lyplg_type_validate_union,
+ .plugin.compare = lyplg_type_compare_union,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_union,
+ .plugin.duplicate = lyplg_type_dup_union,
+ .plugin.free = lyplg_type_free_union,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};
diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c
new file mode 100644
index 0000000..a15e5b7
--- /dev/null
+++ b/src/plugins_types/xpath1.0.c
@@ -0,0 +1,521 @@
+/**
+ * @file xpath1.0.c
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief ietf-yang-types xpath1.0 type plugin.
+ *
+ * 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
+ */
+
+#define _GNU_SOURCE
+
+#include "plugins_types.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libyang.h"
+
+#include "common.h"
+#include "compat.h"
+
+/* internal headers */
+#include "xml.h"
+#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 |
+ */
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_xpath10_print_token(const char *token, uint16_t tok_len, ly_bool is_nametest, const struct lys_module **context_mod,
+ const struct ly_ctx *resolve_ctx, LY_VALUE_FORMAT resolve_format, const void *resolve_prefix_data,
+ LY_VALUE_FORMAT get_format, void *get_prefix_data, char **token_p, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ const char *str_begin, *str_next, *prefix;
+ ly_bool is_prefix, has_prefix = 0;
+ char *str = NULL;
+ void *mem;
+ uint32_t len, str_len = 0, pref_len;
+ const struct lys_module *mod;
+
+ str_begin = token;
+
+ while (!(ret = ly_value_prefix_next(str_begin, token + tok_len, &len, &is_prefix, &str_next)) && len) {
+ if (!is_prefix) {
+ if (!has_prefix && is_nametest && (get_format == LY_VALUE_XML) && *context_mod) {
+ /* prefix is always needed, get it in the target format */
+ prefix = lyplg_type_get_prefix(*context_mod, get_format, get_prefix_data);
+ if (!prefix) {
+ ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
+ goto cleanup;
+ }
+
+ /* append the nametest and prefix */
+ mem = realloc(str, str_len + strlen(prefix) + 1 + len + 1);
+ LY_CHECK_ERR_GOTO(!mem, ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory."), cleanup);
+ str = mem;
+ str_len += sprintf(str + str_len, "%s:%.*s", prefix, len, str_begin);
+ } else {
+ /* just append the string, we may get the first expression node without a prefix but since this
+ * is not strictly forbidden, allow it */
+ mem = realloc(str, str_len + len + 1);
+ LY_CHECK_ERR_GOTO(!mem, ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory."), cleanup);
+ str = mem;
+ str_len += sprintf(str + str_len, "%.*s", len, str_begin);
+ }
+ } else {
+ /* remember there was a prefix found */
+ has_prefix = 1;
+
+ /* resolve the module in the original format */
+ mod = lyplg_type_identity_module(resolve_ctx, NULL, str_begin, len, resolve_format, resolve_prefix_data);
+ if (!mod && is_nametest) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL, "Failed to resolve prefix \"%.*s\".", len, str_begin);
+ goto cleanup;
+ }
+
+ if (is_nametest && ((get_format == LY_VALUE_JSON) || (get_format == LY_VALUE_LYB)) && (*context_mod == mod)) {
+ /* inherit the prefix and do not print it again */
+ } else {
+ if (mod) {
+ /* get the prefix in the target format */
+ prefix = lyplg_type_get_prefix(mod, get_format, get_prefix_data);
+ if (!prefix) {
+ ret = ly_err_new(err, LY_EINT, LYVE_DATA, NULL, NULL, "Internal error.");
+ goto cleanup;
+ }
+ pref_len = strlen(prefix);
+ } else {
+ /* invalid prefix, just copy it */
+ prefix = str_begin;
+ pref_len = len;
+ }
+
+ /* append the prefix */
+ mem = realloc(str, str_len + pref_len + 2);
+ LY_CHECK_ERR_GOTO(!mem, ret = ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory."), cleanup);
+ str = mem;
+ str_len += sprintf(str + str_len, "%.*s:", (int)pref_len, prefix);
+ }
+
+ if (is_nametest) {
+ /* update context module */
+ *context_mod = mod;
+ }
+ }
+
+ str_begin = str_next;
+ }
+
+cleanup:
+ if (ret) {
+ free(str);
+ } else {
+ *token_p = str;
+ }
+ return ret;
+}
+
+/**
+ * @brief Print xpath1.0 subexpression in the specific format.
+ *
+ * @param[in,out] cur_idx Current index of the next token in the expression.
+ * @param[in] end_tok End token (including) that finishes this subexpression parsing. If 0, parse until the end.
+ * @param[in] context_mod Current context module, some formats (::LY_VALUE_JSON and ::LY_VALUE_LYB) inherit this module
+ * instead of printing it again.
+ * @param[in] xp_val xpath1.0 value structure.
+ * @param[in] format Format to print in.
+ * @param[in] prefix_data Format-specific prefix data.
+ * @param[in,out] str_value Printed value, appended to.
+ * @param[in,out] str_len Length of @p str_value, updated.
+ * @param[out] err Error structure on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+xpath10_print_subexpr_r(uint16_t *cur_idx, enum lyxp_token end_tok, const struct lys_module *context_mod,
+ const struct lyd_value_xpath10 *xp_val, LY_VALUE_FORMAT format, void *prefix_data, char **str_value,
+ uint32_t *str_len, struct ly_err_item **err)
+{
+ enum lyxp_token cur_tok, sub_end_tok;
+ char *str_tok;
+ void *mem;
+ const char *cur_exp_ptr;
+ ly_bool is_nt;
+ const struct lys_module *orig_context_mod = context_mod;
+
+ while (*cur_idx < xp_val->exp->used) {
+ cur_tok = xp_val->exp->tokens[*cur_idx];
+ cur_exp_ptr = xp_val->exp->expr + xp_val->exp->tok_pos[*cur_idx];
+
+ if ((cur_tok == LYXP_TOKEN_NAMETEST) || (cur_tok == LYXP_TOKEN_LITERAL)) {
+ /* tokens that may include prefixes, get them in the target format */
+ is_nt = (cur_tok == LYXP_TOKEN_NAMETEST) ? 1 : 0;
+ LY_CHECK_RET(lyplg_type_xpath10_print_token(cur_exp_ptr, xp_val->exp->tok_len[*cur_idx], is_nt, &context_mod,
+ xp_val->ctx, xp_val->format, xp_val->prefix_data, format, prefix_data, &str_tok, err));
+
+ /* append the converted token */
+ mem = realloc(*str_value, *str_len + strlen(str_tok) + 1);
+ LY_CHECK_ERR_GOTO(!mem, free(str_tok), error_mem);
+ *str_value = mem;
+ *str_len += sprintf(*str_value + *str_len, "%s", str_tok);
+ free(str_tok);
+
+ /* token processed */
+ ++(*cur_idx);
+ } else {
+ if ((cur_tok == LYXP_TOKEN_OPER_LOG) || (cur_tok == LYXP_TOKEN_OPER_UNI) || (cur_tok == LYXP_TOKEN_OPER_MATH)) {
+ /* copy the token with spaces around */
+ mem = realloc(*str_value, *str_len + 1 + xp_val->exp->tok_len[*cur_idx] + 2);
+ LY_CHECK_GOTO(!mem, error_mem);
+ *str_value = mem;
+ *str_len += sprintf(*str_value + *str_len, " %.*s ", (int)xp_val->exp->tok_len[*cur_idx], cur_exp_ptr);
+
+ /* reset context mod */
+ context_mod = orig_context_mod;
+ } else {
+ /* just copy the token */
+ mem = realloc(*str_value, *str_len + xp_val->exp->tok_len[*cur_idx] + 1);
+ LY_CHECK_GOTO(!mem, error_mem);
+ *str_value = mem;
+ *str_len += sprintf(*str_value + *str_len, "%.*s", (int)xp_val->exp->tok_len[*cur_idx], cur_exp_ptr);
+ }
+
+ /* token processed but keep it in cur_tok */
+ ++(*cur_idx);
+
+ if (end_tok && (cur_tok == end_tok)) {
+ /* end token found */
+ break;
+ } else if ((cur_tok == LYXP_TOKEN_BRACK1) || (cur_tok == LYXP_TOKEN_PAR1)) {
+ sub_end_tok = (cur_tok == LYXP_TOKEN_BRACK1) ? LYXP_TOKEN_BRACK2 : LYXP_TOKEN_PAR2;
+
+ /* parse the subexpression separately, use the current context mod */
+ LY_CHECK_RET(xpath10_print_subexpr_r(cur_idx, sub_end_tok, context_mod, xp_val, format, prefix_data,
+ str_value, str_len, err));
+ }
+ }
+ }
+
+ return LY_SUCCESS;
+
+error_mem:
+ return ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, "No memory.");
+}
+
+LIBYANG_API_DEF LY_ERR
+lyplg_type_print_xpath10_value(const struct lyd_value_xpath10 *xp_val, LY_VALUE_FORMAT format, void *prefix_data,
+ char **str_value, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ uint16_t expr_idx = 0;
+ uint32_t str_len = 0;
+
+ *str_value = NULL;
+ *err = NULL;
+
+ /* recursively print the expression */
+ ret = xpath10_print_subexpr_r(&expr_idx, 0, NULL, xp_val, format, prefix_data, str_value, &str_len, err);
+
+ if (ret) {
+ free(*str_value);
+ *str_value = NULL;
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF 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 *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_xpath10 *val;
+ char *canon;
+
+ /* init storage */
+ memset(storage, 0, sizeof *storage);
+ LYPLG_TYPE_VAL_INLINE_PREPARE(storage, val);
+ LY_CHECK_ERR_GOTO(!val, ret = LY_EMEM, cleanup);
+ storage->realtype = type;
+
+ /* 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);
+
+ /* parse */
+ ret = lyxp_expr_parse(ctx, value_len ? value : "", value_len, 1, &val->exp);
+ LY_CHECK_GOTO(ret, cleanup);
+ val->ctx = ctx;
+
+ if (ctx_node && !strcmp(ctx_node->name, "parent-reference") && !strcmp(ctx_node->module->name, "ietf-yang-schema-mount")) {
+ /* special case, this type uses prefix-namespace mapping provided directly in data, keep empty for now */
+ val->format = format = LY_VALUE_STR_NS;
+ ret = ly_set_new((struct ly_set **)&val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ } else {
+ /* store format-specific data and context for later prefix resolution */
+ ret = lyplg_type_prefix_data_new(ctx, value, value_len, format, prefix_data, &val->format, &val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+
+ switch (format) {
+ case LY_VALUE_CANON:
+ case LY_VALUE_JSON:
+ case LY_VALUE_LYB:
+ case LY_VALUE_STR_NS:
+ /* 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);
+ }
+ break;
+ case LY_VALUE_SCHEMA:
+ case LY_VALUE_SCHEMA_RESOLVED:
+ case LY_VALUE_XML:
+ /* JSON format with prefix is the canonical one */
+ ret = lyplg_type_print_xpath10_value(val, LY_VALUE_JSON, NULL, &canon, err);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lydict_insert_zc(ctx, canon, &storage->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+ break;
+ }
+
+cleanup:
+ if (options & LYPLG_TYPE_STORE_DYNAMIC) {
+ free((void *)value);
+ }
+
+ if (ret) {
+ lyplg_type_free_xpath10(ctx, storage);
+ } else if (val->format == LY_VALUE_STR_NS) {
+ /* needs validation */
+ return LY_EINCOMPLETE;
+ }
+ return ret;
+}
+
+/**
+ * @brief Implementation of ::lyplg_type_validate_clb for the xpath1.0 ietf-yang-types type.
+ */
+static LY_ERR
+lyplg_type_validate_xpath10(const struct ly_ctx *UNUSED(ctx), const struct lysc_type *UNUSED(type),
+ const struct lyd_node *ctx_node, const struct lyd_node *UNUSED(tree), struct lyd_value *storage,
+ struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+ struct lyd_value_xpath10 *val;
+ struct ly_set *set = NULL;
+ uint32_t i;
+ const char *pref, *uri;
+ struct lyxml_ns *ns;
+
+ *err = NULL;
+ LYD_VALUE_GET(storage, val);
+
+ if (val->format != LY_VALUE_STR_NS) {
+ /* nothing to validate */
+ return LY_SUCCESS;
+ }
+
+ /* the XML namespace set must exist */
+ assert(val->prefix_data);
+
+ /* special handling of this particular node */
+ assert(!strcmp(LYD_NAME(ctx_node), "parent-reference") &&
+ !strcmp(ctx_node->schema->module->name, "ietf-yang-schema-mount"));
+
+ /* get all the prefix mappings */
+ if ((ret = lyd_find_xpath(ctx_node, "../../../namespace", &set))) {
+ goto cleanup;
+ }
+
+ for (i = 0; i < set->count; ++i) {
+ assert(!strcmp(LYD_NAME(lyd_child(set->dnodes[i])), "prefix"));
+ pref = lyd_get_value(lyd_child(set->dnodes[i]));
+
+ if (!lyd_child(set->dnodes[i])->next) {
+ /* missing URI - invalid mapping, skip */
+ continue;
+ }
+ assert(!strcmp(LYD_NAME(lyd_child(set->dnodes[i])->next), "uri"));
+ uri = lyd_get_value(lyd_child(set->dnodes[i])->next);
+
+ /* create new ns */
+ ns = calloc(1, sizeof *ns);
+ if (!ns) {
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ ns->prefix = strdup(pref);
+ ns->uri = strdup(uri);
+ if (!ns->prefix || !ns->uri) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ ret = LY_EMEM;
+ goto cleanup;
+ }
+ ns->depth = 1;
+
+ /* add into the XML namespace set */
+ if ((ret = ly_set_add(val->prefix_data, ns, 1, NULL))) {
+ free(ns->prefix);
+ free(ns->uri);
+ free(ns);
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ ly_set_free(set, NULL);
+ if (ret == LY_EMEM) {
+ ly_err_new(err, LY_EMEM, LYVE_DATA, NULL, NULL, LY_EMEM_MSG);
+ } else if (ret) {
+ ly_err_new(err, ret, LYVE_DATA, NULL, NULL, "%s", ly_errmsg(LYD_CTX(ctx_node)));
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF 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)
+{
+ struct lyd_value_xpath10 *val;
+ char *ret;
+ struct ly_err_item *err = NULL;
+
+ LYD_VALUE_GET(value, val);
+
+ /* LY_VALUE_STR_NS should never be transformed */
+ if ((val->format == LY_VALUE_STR_NS) || (format == LY_VALUE_CANON) || (format == LY_VALUE_JSON) ||
+ (format == LY_VALUE_LYB)) {
+ /* canonical */
+ if (dynamic) {
+ *dynamic = 0;
+ }
+ if (value_len) {
+ *value_len = strlen(value->_canonical);
+ }
+ return value->_canonical;
+ }
+
+ /* print in the specific format */
+ if (lyplg_type_print_xpath10_value(val, format, prefix_data, &ret, &err)) {
+ if (err) {
+ LOGVAL_ERRITEM(ctx, err);
+ ly_err_free(err);
+ }
+ return NULL;
+ }
+
+ *dynamic = 1;
+ if (value_len) {
+ *value_len = strlen(ret);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF 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 *orig_val, *dup_val;
+
+ /* init dup value */
+ memset(dup, 0, sizeof *dup);
+ dup->realtype = original->realtype;
+
+ ret = lydict_insert(ctx, original->_canonical, 0, &dup->_canonical);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ LYPLG_TYPE_VAL_INLINE_PREPARE(dup, dup_val);
+ LY_CHECK_ERR_GOTO(!dup_val, LOGMEM(ctx); ret = LY_EMEM, cleanup);
+ dup_val->ctx = ctx;
+
+ LYD_VALUE_GET(original, orig_val);
+ ret = lyxp_expr_dup(ctx, orig_val->exp, 0, 0, &dup_val->exp);
+ LY_CHECK_GOTO(ret, cleanup);
+
+ ret = lyplg_type_prefix_data_dup(ctx, orig_val->format, orig_val->prefix_data, &dup_val->prefix_data);
+ LY_CHECK_GOTO(ret, cleanup);
+ dup_val->format = orig_val->format;
+
+cleanup:
+ if (ret) {
+ lyplg_type_free_xpath10(ctx, dup);
+ }
+ return ret;
+}
+
+LIBYANG_API_DEF void
+lyplg_type_free_xpath10(const struct ly_ctx *ctx, struct lyd_value *value)
+{
+ struct lyd_value_xpath10 *val;
+
+ lydict_remove(ctx, value->_canonical);
+ value->_canonical = NULL;
+ LYD_VALUE_GET(value, val);
+ if (val) {
+ lyxp_expr_free(ctx, val->exp);
+ lyplg_type_prefix_data_free(val->format, val->prefix_data);
+
+ LYPLG_TYPE_VAL_INLINE_DESTROY(val);
+ }
+}
+
+/**
+ * @brief Plugin information for xpath1.0 type implementation.
+ *
+ * Note that external plugins are supposed to use:
+ *
+ * LYPLG_TYPES = {
+ */
+const struct lyplg_type_record plugins_xpath10[] = {
+ {
+ .module = "ietf-yang-types",
+ .revision = "2013-07-15",
+ .name = "xpath1.0",
+
+ .plugin.id = "libyang 2 - xpath1.0, version 1",
+ .plugin.store = lyplg_type_store_xpath10,
+ .plugin.validate = lyplg_type_validate_xpath10,
+ .plugin.compare = lyplg_type_compare_simple,
+ .plugin.sort = NULL,
+ .plugin.print = lyplg_type_print_xpath10,
+ .plugin.duplicate = lyplg_type_dup_xpath10,
+ .plugin.free = lyplg_type_free_xpath10,
+ .plugin.lyb_data_len = -1,
+ },
+ {0}
+};