From 4f426f9f78ae4401e3e8fed3dde85c45a5fd8fa1 Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Sat, 19 Jul 2025 13:03:16 +0530 Subject: [PATCH 1/8] feat: add initial CBOR format support with libcbor integration --- CMakeLists.txt | 45 +- src/parser_cbor.c | 1076 ++++++++++++++++++++++++++++++++++++++ src/parser_cbor.h | 279 ++++++++++ src/parser_data.h | 17 + src/parser_json.c | 707 ++++++++++++------------- src/printer_data.c | 3 + src/tree.h | 1 + src/tree_data.c | 28 + src/tree_data.h | 19 +- src/tree_data_internal.h | 2 + 10 files changed, 1793 insertions(+), 384 deletions(-) create mode 100644 src/parser_cbor.c create mode 100644 src/parser_cbor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 72c56694d..590847d93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,7 @@ set(format_sources src/*.h src/plugins_exts/* src/plugins_types/*) + # # options # @@ -256,6 +257,7 @@ option(ENABLE_YANGLINT_INTERACTIVE "Enable interactive CLI yanglint" ON) option(ENABLE_TOOLS "Build binary tools 'yanglint' and 'yangre'" ON) option(ENABLE_COMMON_TARGETS "Define common custom target names such as 'doc' or 'uninstall', may cause conflicts when using add_subdirectory() to build this project" ON) option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON) +option(ENABLE_CBOR_SUPPORT "Enable CBOR support with libcbor" ON) set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/libyang" CACHE STRING "Directory where to copy the YANG modules to") if(ENABLE_INTERNAL_DOCS) @@ -323,6 +325,42 @@ if(ENABLE_COVERAGE) gen_coverage_enable(${ENABLE_TESTS}) endif() +if(ENABLE_CBOR_SUPPORT) + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(LIBCBOR REQUIRED libcbor) + if(LIBCBOR_FOUND) + message(STATUS "libcbor found, enabling CBOR support") + add_definitions(-DENABLE_CBOR_SUPPORT) + include_directories(${LIBCBOR_INCLUDE_DIRS}) + # Add CBOR parser files to the library sources + list(APPEND libsrc src/parser_cbor.c) + list(APPEND headers src/parser_cbor.h) + # Add CBOR files to format sources + list(APPEND format_sources src/parser_cbor.c src/parser_cbor.h) + else() + message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") + endif() + else() + # Fallback to find_path and find_library if pkg-config is not available + find_path(LIBCBOR_INCLUDE_DIR cbor.h) + find_library(LIBCBOR_LIBRARY cbor) + if(LIBCBOR_INCLUDE_DIR AND LIBCBOR_LIBRARY) + message(STATUS "libcbor found via find_path/find_library, enabling CBOR support") + add_definitions(-DENABLE_CBOR_SUPPORT) + include_directories(${LIBCBOR_INCLUDE_DIR}) + set(LIBCBOR_LIBRARIES ${LIBCBOR_LIBRARY}) + # Add CBOR parser files to the library sources + list(APPEND libsrc src/parser_cbor.c) + list(APPEND headers src/parser_cbor.h) + # Add CBOR files to format sources + list(APPEND format_sources src/parser_cbor.c src/parser_cbor.h) + else() + message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") + endif() + endif() +endif() + if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") # enable before adding tests to let them detect that format checking is available - one of the tests is format checking source_format_enable(0.77) @@ -427,6 +465,11 @@ find_package(PCRE2 10.21 REQUIRED) include_directories(${PCRE2_INCLUDE_DIRS}) target_link_libraries(yang ${PCRE2_LIBRARIES}) +# link libcbor if CBOR support is enabled +if(ENABLE_CBOR_SUPPORT) + target_link_libraries(yang ${LIBCBOR_LIBRARIES}) +endif() + # XXHash include and library find_package(XXHash) if(XXHASH_FOUND) @@ -521,4 +564,4 @@ add_custom_target(cclean COMMAND make clean COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -not -path './CMakeModules*' -exec rm -rf {} + COMMAND rm -rf Makefile Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/src/parser_cbor.c b/src/parser_cbor.c new file mode 100644 index 000000000..38104f31e --- /dev/null +++ b/src/parser_cbor.c @@ -0,0 +1,1076 @@ +/** + * @file parser_cbor.c + * @author + * @brief CBOR data parser for libyang + * + * Copyright (c) 2020 - 2023 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 + +#ifdef ENABLE_CBOR_SUPPORT + +#include "parser_cbor.h" + +#include +#include +#include +#include +#include + +#include "compat.h" +#include "context.h" +#include "dict.h" +#include "in_internal.h" +#include "log.h" +#include "ly_common.h" +#include "parser_data.h" +#include "parser_internal.h" +#include "plugins_exts.h" +#include "set.h" +#include "tree.h" +#include "tree_data.h" +#include "tree_data_internal.h" +#include "tree_schema.h" +#include "validation.h" + +#include "cbor.h" + + +#include +#include + +void print_json(cbor_item_t *item); + +void print_json_string(const cbor_item_t *item) { + size_t length = cbor_string_length(item); + char *str = (char *)cbor_string_handle(item); + printf("\"%.*s\"", (int)length, str); +} + +void print_json_map(const cbor_item_t *item) { + printf("{"); + size_t size = cbor_map_size(item); + struct cbor_pair *pairs = cbor_map_handle(item); + + for (size_t i = 0; i < size; ++i) { + print_json(pairs[i].key); + printf(": "); + print_json(pairs[i].value); + if (i < size - 1) printf(", "); + } + + printf("}"); +} + +void print_json_bool(const cbor_item_t *item) { + printf(cbor_is_bool(item) && cbor_ctrl_value(item) ? "true" : "false"); +} + +void print_json(cbor_item_t *item) { + if (cbor_isa_map(item)) { + print_json_map(item); + } else if (cbor_isa_string(item)) { + print_json_string(item); + } else if (cbor_is_bool(item)) { + print_json_bool(item); + } else { + printf("null"); // fallback for unsupported types + } +} + +/** + * @brief Free the CBOR parser context + * + * @param[in] lydctx Data parser context to free. + */ +static void +lyd_cbor_ctx_free(struct lyd_ctx *lydctx) +{ + struct lyd_cbor_ctx *ctx = (struct lyd_cbor_ctx *)lydctx; + + if(lydctx){ + lyd_ctx_free(lydctx); + lycbor_ctx_free(ctx->cborctx); + free(ctx); + } +} + +/** + * @brief Create new CBOR context for parsing. + * + * @param[in] ctx libyang context. + * @param[in] in Input handler. + * @param[out] cbor_ctx_p Pointer to store the created CBOR context. + * @return LY_ERR value. + */ +LY_ERR +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lycbor_ctx *cbor_ctx; + + assert(ctx && in && cbor_ctx_p); + + /* Allocate and initialize CBOR context */ + cbor_ctx = calloc(1, sizeof *cbor_ctx); + LY_CHECK_ERR_RET(!cbor_ctx, LOGMEM(ctx), LY_EMEM); + + cbor_ctx->ctx = ctx; + cbor_ctx->in = in; + + *cbor_ctx_p = cbor_ctx; + return ret; +} + +/** + * @brief Free CBOR context. + * + * @param[in] cbor_ctx CBOR context to free. + */ +void +lycbor_ctx_free(struct lycbor_ctx *cbor_ctx) +{ + if (cbor_ctx) { + free(cbor_ctx); + } +} + +/** + * @brief Create new CBOR parser context + */ +LY_ERR +lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, + uint32_t parse_opts, uint32_t val_opts, enum lyd_cbor_format format, + struct lyd_cbor_ctx **lydctx_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx = NULL; + + assert(lydctx_p); + + /* Initialize context with calloc to ensure all fields are zero */ + lydctx = calloc(1, sizeof *lydctx); + LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM); + lydctx->parse_opts = parse_opts; + lydctx->val_opts = val_opts; + lydctx->free = lyd_cbor_ctx_free; + lydctx->format = format; + + lydctx->cborctx = NULL; /* Will be set below */ + + /* Create low-level CBOR context */ + LY_CHECK_GOTO(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), cleanup); + + *lydctx_p = lydctx; + return ret; + +cleanup: + if (lydctx) { + lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); + } + return ret; +} + + +/** + * @brief Convert a CBOR item to a string representation. + * + * This function handles the low-level CBOR to string conversion, + * similar to how JSON parser converts JSON values to strings. + * + * @param[in] item CBOR item to convert. + * @param[out] str_val String value (allocated, caller must free). + * @param[out] str_len String length. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) +{ + LY_ERR ret = LY_SUCCESS; + + assert(item && str_val && str_len); + *str_val = NULL; + *str_len = 0; + + switch (cbor_typeof(item)) + { + case CBOR_TYPE_UINT: + { + uint64_t val = cbor_get_int(item); + int len = snprintf(NULL, 0, "%" PRIu64, val); + if (len < 0) { + return LY_ESYS; + } + *str_val = malloc(len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + sprintf(*str_val, "%" PRIu64, val); + *str_len = len; + break; + } + case CBOR_TYPE_NEGINT: + { + int64_t val = -1 - (int64_t)cbor_get_int(item); + int len = snprintf(NULL, 0, "%" PRId64, val); + if (len < 0) { + return LY_ESYS; + } + *str_val = malloc(len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + sprintf(*str_val, "%" PRId64, val); + *str_len = len; + break; + } + case CBOR_TYPE_BYTESTRING: + *str_len = cbor_bytestring_length(item); + *str_val = malloc(*str_len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + memcpy(*str_val, cbor_bytestring_handle(item), *str_len); + (*str_val)[*str_len] = '\0'; + break; + case CBOR_TYPE_STRING: + *str_len = cbor_string_length(item); + *str_val = malloc(*str_len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + memcpy(*str_val, cbor_string_handle(item), *str_len); + (*str_val)[*str_len] = '\0'; + break; + case CBOR_TYPE_FLOAT_CTRL: + if (cbor_float_ctrl_is_ctrl(item)) + { + switch (cbor_ctrl_value(item)) + { + case CBOR_CTRL_TRUE: + *str_val = strdup("true"); + *str_len = 4; + break; + case CBOR_CTRL_FALSE: + *str_val = strdup("false"); + *str_len = 5; + break; + case CBOR_CTRL_NULL: + *str_val = strdup(""); + *str_len = 0; + break; + default: + LOGVAL(NULL, LYVE_SYNTAX, "Unsupported CBOR control value"); + ret = LY_EVALID; + break; + } + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + } + else + { + /* Float value */ + double val = cbor_float_get_float(item); + int len = snprintf(NULL, 0, "%g", val); + if (len < 0) { + return LY_ESYS; + } + *str_val = malloc(len + 1); + LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); + sprintf(*str_val, "%g", val); + *str_len = len; + } + break; + default: + LOGVAL(NULL, LYVE_SYNTAX, "Unsupported CBOR data type %d", cbor_typeof(item)); + ret = LY_EVALID; + break; + } + + return ret; +} + +/** + * @brief Get string key from CBOR map item. + * + * For named identifier format, keys should be strings. + * For SID format, keys would be integers (future implementation). + */ +static LY_ERR +lydcbor_get_key_string(struct lyd_cbor_ctx *lydctx, const cbor_item_t *key_item, + char **key_str, size_t *key_len) +{ + LY_ERR ret = LY_SUCCESS; + + assert(lydctx && key_item && key_str && key_len); + + switch (lydctx->format) + { + case LYD_CBOR_NAMED: + /* Keys must be strings for named format */ + if (!cbor_isa_string(key_item)) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); + return LY_EVALID; + } + ret = lydcbor_item_to_string(key_item, key_str, key_len); + break; + case LYD_CBOR_SID: + /* Future: Handle SID integer keys */ + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "CBOR SID format not yet implemented"); + ret = LY_ENOT; + break; + default: + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unknown CBOR format"); + ret = LY_EINVAL; + break; + } + + return ret; +} + +LY_ERR +lydcbor_parse_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const void *cbor_item, struct lyd_node **node) +{ + LY_ERR ret = LY_SUCCESS; + const cbor_item_t *item = (const cbor_item_t *)cbor_item; + char *str_val = NULL; + size_t str_len = 0; + + assert(lydctx && snode && item && node); + + /* Convert CBOR value to string */ + LY_CHECK_GOTO(ret = lydcbor_item_to_string(item, &str_val, &str_len), cleanup); + + /* Create data node based on schema node type */ + switch (snode->nodetype) + { + case LYS_LEAF: + case LYS_LEAFLIST: + ret = lyd_create_term(snode, str_val, str_len, 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, node); + break; + case LYS_ANYDATA: + case LYS_ANYXML: + /* For anydata/anyxml, we store the CBOR directly */ + ret = lyd_create_any(snode, cbor_item, LYD_ANYDATA_CBOR, 0, node); + break; + default: + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type for CBOR value"); + ret = LY_EVALID; + break; + } + +cleanup: + free(str_val); + return ret; +} + + +/** + * @brief Get module prefix from a qualified name. + * + * @param[in] qname Qualified name (prefix:name or just name). + * @param[in] qname_len Length of the qualified name. + * @param[out] prefix Extracted prefix (points into qname, not allocated). + * @param[out] prefix_len Length of the prefix. + * @param[out] name Local name (points into qname, not allocated). + * @param[out] name_len Length of the local name. + * @return LY_SUCCESS on success. + */ +static LY_ERR +lydcbor_parse_qname(const char *qname, size_t qname_len, const char **prefix, size_t *prefix_len, + const char **name, size_t *name_len) +{ + const char *colon; + + assert(qname && name && name_len); + + *name = qname; + *name_len = qname_len; + + if (prefix) { + *prefix = NULL; + } + if (prefix_len) { + *prefix_len = 0; + } + + /* Look for module prefix separator */ + colon = ly_strnchr(qname, ':', qname_len); + if (colon) { + /* We have a module prefix */ + if (prefix) { + *prefix = qname; + *prefix_len = colon - qname; + } + + /* Local name starts after the colon */ + *name = colon + 1; + *name_len = qname_len - (colon - qname) - 1; + + /* Validate we have both prefix and name */ + if ((colon == qname) || (*name_len == 0)) { + return LY_EVALID; + } + } + + return LY_SUCCESS; +} + +/** + * @brief Get schema node from CBOR node name, following lydjson_get_snode logic. + * + * @param[in] lydctx CBOR parser context. + * @param[in] name Node name. + * @param[in] name_len Length of node name. + * @param[in] parent Data parent node. + * @param[out] snode Schema node found. + * @param[out] ext Extension instance if found. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, + struct lyd_node *parent, const struct lysc_node **snode, + const struct lysc_ext_instance **ext) +{ + LY_ERR ret = LY_SUCCESS, r; + const char *prefix = NULL, *local_name = NULL; + size_t prefix_len = 0, local_name_len = 0; + const struct lys_module *mod = NULL; + const struct lysc_node *sparent = NULL; + uint32_t getnext_opts; + + assert(lydctx && name && snode); + *snode = NULL; + if (ext) { + *ext = NULL; + } + + /* Parse qualified name */ + LY_CHECK_RET(lydcbor_parse_qname(name, name_len, &prefix, &prefix_len, &local_name, &local_name_len)); + + /* Get parent schema node */ + if (parent) { + sparent = parent->schema; + if (!sparent) { + /* Opaque parent */ + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Cannot parse \"%.*s\" node with opaque parent.", + (int)local_name_len, local_name); + ret = LY_EVALID; + goto cleanup; + } + } else { + sparent = NULL; + } + + /* Resolve module if prefix is present */ + if (prefix) { + mod = ly_ctx_get_module_implemented2(lydctx->cborctx->ctx, prefix, prefix_len); + if (!mod) { + if (lydctx->parse_opts & LYD_PARSE_STRICT) { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); + ret = LY_EVALID; + goto cleanup; + } + if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); + ret = LY_EVALID; + goto cleanup; + } + } + } else if (!sparent) { + /* Top-level node without prefix - need to find module */ + /* Try to find the node in all implemented modules */ + const struct lys_module *iter_mod; + uint32_t idx = 0; + ly_bool found = 0; + + while ((iter_mod = ly_ctx_get_module_iter(lydctx->cborctx->ctx, &idx))) { + if (!iter_mod->implemented) { + continue; + } + + /* Check if node exists in this module */ + if (lys_find_child(NULL, iter_mod, local_name, local_name_len, 0, 0)) { + if (found) { + /* Ambiguous name */ + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Ambiguous node name \"%.*s\", use module prefix.", + (int)local_name_len, local_name); + ret = LY_EVALID; + goto cleanup; + } + mod = iter_mod; + found = 1; + } + } + + if (!found && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unknown node \"%.*s\".", (int)local_name_len, local_name); + ret = LY_EVALID; + goto cleanup; + } + } + + /* Set getnext options */ + getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; + if (parent && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) { + if (lydctx->int_opts & LYD_INTOPT_RPC) { + getnext_opts = 0; + } else if (lydctx->int_opts & LYD_INTOPT_REPLY) { + getnext_opts = LYS_GETNEXT_OUTPUT; + } + } + + /* Find schema node */ + if (sparent) { + /* Search in parent's children */ + *snode = lys_find_child(sparent, sparent->module, local_name, local_name_len, 0, getnext_opts); + + /* Try to find extension data if regular node not found */ + if (!*snode && ext) { + r = ly_nested_ext_schema(parent, sparent, prefix, prefix_len, LY_VALUE_JSON, NULL, + local_name, local_name_len, snode, ext); + if (r != LY_ENOT) { + if (r) { + ret = r; + goto cleanup; + } + } + } + } else { + /* Top-level node */ + if (mod) { + /* Search in specific module */ + *snode = lys_find_child(NULL, mod, local_name, local_name_len, 0, getnext_opts); + } + /* Extension data for top-level not typically handled */ + } + + /* Handle missing schema node */ + if (!*snode) { + if (lydctx->parse_opts & LYD_PARSE_STRICT) { + if (prefix) { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", + (int)local_name_len, local_name, (int)prefix_len, prefix); + } else { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", + (int)local_name_len, local_name); + } + ret = LY_EVALID; + goto cleanup; + } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) { + /* Log error but continue if not in strict mode and opaque allowed */ + if (prefix) { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", + (int)local_name_len, local_name, (int)prefix_len, prefix); + } else { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", + (int)local_name_len, local_name); + } + ret = LY_EVALID; + goto cleanup; + } + /* If opaque parsing allowed, *snode remains NULL and caller handles it */ + } + +cleanup: + return ret; +} + +static LY_ERR +lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + struct lyd_node **node, const cbor_item_t *cbor_value) +{ + LY_ERR ret = LY_SUCCESS; + + assert(lydctx && snode && node && cbor_value); + *node = NULL; + + switch (snode->nodetype) { + case LYS_CONTAINER: + ret = lyd_create_inner(snode, node); + break; + case LYS_LIST: + ret = lyd_create_inner(snode, node); + break; + case LYS_LEAF: + case LYS_LEAFLIST: { + char *str_val = NULL; + size_t str_len = 0; + + ret = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + if (ret == LY_SUCCESS) { + ret = lyd_create_term(snode, str_val, str_len, 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, node); + } + free(str_val); + break; + } + case LYS_ANYDATA: + case LYS_ANYXML: + ret = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, node); + break; + default: + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type %d", snode->nodetype); + ret = LY_EVALID; + break; + } + + return ret; +} + +static LY_ERR +lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_node *node = NULL; + size_t array_size; + cbor_item_t **array_handle; + + assert(lydctx && snode && array_item && parsed); + + if (!cbor_isa_array(array_item)) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for leaf-list"); + return LY_EVALID; + } + + if (snode->nodetype != LYS_LEAFLIST) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be leaf-list"); + return LY_EVALID; + } + + array_size = cbor_array_size(array_item); + array_handle = cbor_array_handle(array_item); + + if (!array_handle && array_size > 0) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); + return LY_EVALID; + } + + for (size_t i = 0; i < array_size; ++i) { + const cbor_item_t *item = array_handle[i]; + + if (!item) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + LY_CHECK_GOTO(ret = lydcbor_parse_node_value(lydctx, snode, &node, item), cleanup); + + if (!node) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create node for array element %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + /* Insert the node */ + ret = lyd_insert_sibling(*first_p, node, first_p); + LY_CHECK_GOTO(ret, cleanup); + + /* Add to parsed set */ + LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); + node = NULL; /* Reset pointer after successful insertion */ + } + +cleanup: + if (ret && node) { + lyd_free_tree(node); + } + return ret; +} + +static LY_ERR +lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj) +{ + LY_ERR ret = LY_SUCCESS; + + printf("Entering lydcbor_parse_subtree\n"); + printf("CBOR object:\n"); + print_json(cbor_obj); + printf("\n"); + + const struct lysc_node *snode = NULL; + char *key_str = NULL; + size_t key_len = 0; +\ + assert(lydctx && first_p && parsed && cbor_obj); + + /* assuming that the top level structure is always a map + to be modified to include anything else that it can support */ + + if (!cbor_isa_map(cbor_obj)) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR map"); + return LY_EVALID; + } + + size_t map_size = cbor_map_size(cbor_obj); + struct cbor_pair *pairs = cbor_map_handle(cbor_obj); + + if (!pairs && map_size > 0) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR map structure"); + return LY_EVALID; + } + + for (size_t i = 0; i < map_size; ++i) { + const cbor_item_t *key_item = pairs[i].key; + const cbor_item_t *value_item = pairs[i].value; + + if (!key_item || !value_item) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null key or value at map index %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + /* Get key string */ + LY_CHECK_GOTO(ret = lydcbor_get_key_string(lydctx, key_item, &key_str, &key_len), cleanup); + + /* Find schema node */ + LY_CHECK_GOTO(ret = lydcbor_get_snode(lydctx, key_str, key_len, + parent, &snode, NULL), cleanup); + + /* Handle different node types */ + if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) { + if (snode->nodetype == LYS_LEAFLIST && cbor_isa_array(value_item)) { + ret = lydcbor_parse_leaflist_array(lydctx, snode, value_item, first_p, parsed); + } else { + ret = lydcbor_parse_terminal(lydctx, snode, value_item, first_p, parsed); + } + } else if (snode->nodetype == LYS_CONTAINER) { + ret = lydcbor_parse_container(lydctx, snode, value_item, first_p, parsed); + } else if (snode->nodetype == LYS_LIST) { + ret = lydcbor_parse_list(lydctx, snode, value_item, first_p, parsed); + } else if (snode->nodetype & (LYS_ANYDATA | LYS_ANYXML)) { + ret = lydcbor_parse_any(lydctx, snode, value_item, first_p, parsed); + } else { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type %d for \"%s\"", + snode->nodetype, snode->name); + ret = LY_EVALID; + } + + LY_CHECK_GOTO(ret, cleanup); + + free(key_str); + key_str = NULL; + } + +cleanup: + free(key_str); + return ret; +} + +// Add these new functions: + +static LY_ERR +lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret; + struct lyd_node *node = NULL; + + ret = lydcbor_parse_node_value(lydctx, snode, &node, cbor_value); + LY_CHECK_RET(ret); + + if (!node) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create terminal node for \"%s\"", snode->name); + return LY_EVALID; + } + + /* Insert into tree */ + ret = lyd_insert_sibling(*first_p, node, first_p); + if (ret) { + lyd_free_tree(node); + return ret; + } + + /* Add to parsed set */ + return ly_set_add(parsed, node, 1, NULL); +} + +static LY_ERR +lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret; + struct lyd_node *node = NULL; + + ret = lyd_create_inner(snode, &node); + LY_CHECK_RET(ret); + + if (!node) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create container node for \"%s\"", snode->name); + return LY_EVALID; + } + + /* Insert into tree first */ + ret = lyd_insert_sibling(*first_p, node, first_p); + if (ret) { + lyd_free_tree(node); + return ret; + } + + /* Add to parsed set */ + ret = ly_set_add(parsed, node, 1, NULL); + LY_CHECK_RET(ret); + + /* Parse container children */ + if (cbor_isa_map(cbor_value) && cbor_map_size(cbor_value) > 0) { + struct lyd_node *child_first = NULL; + ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, cbor_value); + if (ret) { + return ret; + } + + /* Link children to container */ + if (child_first) { + lyd_insert_child(node, child_first); + } + } + + return LY_SUCCESS; +} + +// Replace lydcbor_parse_list_array with this improved version: +static LY_ERR +lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_node *node = NULL; + size_t array_size; + cbor_item_t **array_handle; + + assert(lydctx && snode && array_item && parsed); + + if (!cbor_isa_array(array_item)) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for list"); + return LY_EVALID; + } + + if (snode->nodetype != LYS_LIST) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be list"); + return LY_EVALID; + } + + array_size = cbor_array_size(array_item); + array_handle = cbor_array_handle(array_item); + + if (!array_handle && array_size > 0) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); + return LY_EVALID; + } + + for (size_t i = 0; i < array_size; ++i) { + const cbor_item_t *item = array_handle[i]; + + if (!item) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + if (!cbor_isa_map(item)) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "List entry must be a CBOR map"); + ret = LY_EVALID; + goto cleanup; + } + + ret = lyd_create_inner(snode, &node); + LY_CHECK_GOTO(ret, cleanup); + + if (!node) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create list node"); + ret = LY_EVALID; + goto cleanup; + } + + /* Insert the list node */ + ret = lyd_insert_sibling(*first_p, node, first_p); + LY_CHECK_GOTO(ret, cleanup); + + /* Add to parsed set */ + LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); + + /* Parse list entry content */ + struct lyd_node *child_first = NULL; + ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, item); + LY_CHECK_GOTO(ret, cleanup); + + /* Link children to list entry */ + if (child_first) { + lyd_insert_child(node, child_first); + } + + node = NULL; /* Reset pointer after successful processing */ + } + +cleanup: + if (ret && node) { + lyd_free_tree(node); + } + return ret; +} + +static LY_ERR +lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret = LY_SUCCESS; + + if (cbor_isa_array(cbor_value)) { + /* Array of list entries */ + ret = lydcbor_parse_list_array(lydctx, snode, cbor_value, first_p, parsed); + } else if (cbor_isa_map(cbor_value)) { + /* Single list entry */ + struct lyd_node *node = NULL; + + ret = lyd_create_inner(snode, &node); + LY_CHECK_RET(ret); + + /* Insert into tree */ + ret = lyd_insert_sibling(*first_p, node, first_p); + if (ret) { + lyd_free_tree(node); + return ret; + } + + /* Add to parsed set */ + ret = ly_set_add(parsed, node, 1, NULL); + LY_CHECK_RET(ret); + + /* Parse list entry content */ + struct lyd_node *child_first = NULL; + ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, cbor_value); + if (ret) { + return ret; + } + + /* Link children to list entry */ + if (child_first) { + lyd_insert_child(node, child_first); + } + } else { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "List \"%s\" value must be a CBOR map or array", snode->name); + ret = LY_EVALID; + } + + return ret; +} + +static LY_ERR +lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret; + struct lyd_node *node = NULL; + + ret = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, &node); + LY_CHECK_RET(ret); + + if (!node) { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create any node for \"%s\"", snode->name); + return LY_EVALID; + } + + /* Insert into tree */ + ret = lyd_insert_sibling(*first_p, node, first_p); + if (ret) { + lyd_free_tree(node); + return ret; + } + + /* Add to parsed set */ + return ly_set_add(parsed, node, 1, NULL); +} + +LY_ERR +lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) +{ + /* Simple heuristic: try to parse as CBOR and examine structure */ + /* For now, default to named format */ + (void)in; + *format = LYD_CBOR_NAMED; + return LY_SUCCESS; +} + +LY_ERR +lydcbor_parse_metadata(struct lyd_cbor_ctx *lydctx, const void *cbor_item, struct lyd_node *node) +{ + /* Future implementation for CBOR metadata parsing */ + (void)lydctx; + (void)cbor_item; + (void)node; + return LY_SUCCESS; +} + +LY_ERR +lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx = NULL; + cbor_item_t *cbor_data = NULL; + struct cbor_load_result result = {0}; + enum lyd_cbor_format format; + + /* Detect CBOR format - Named or SID */ + LY_CHECK_GOTO(ret = lydcbor_detect_format(in, &format), cleanup); + + /* Initialize context */ + LY_CHECK_GOTO(ret = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, format, &lydctx), cleanup); + + lydctx->int_opts = int_opts; + lydctx->ext = ext; + + /* + * Loads CBOR data from the current input buffer. + * + * Parameters: + * in->current - Pointer to the current position in the input buffer. + * in->length - Length of the data to be loaded. + * &result - Pointer to a variable where the result status will be stored. + * + * Returns: + * cbor_data - Pointer to the loaded CBOR data structure, or NULL on failure. + */ + /* need to convert in->current from const char* to cbor_data type */ + cbor_data = cbor_load(in->current, in->length, &result); + lydctx->cborctx->cbor_data = cbor_data; + + if (!cbor_data) { + LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: no data returned from cbor_load()."); + ret = LY_EVALID; + goto cleanup; + } + if (result.error.code != CBOR_ERR_NONE) { + LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: parsing error (code %d).", result.error.code); + ret = LY_EVALID; + goto cleanup; + } + + /* Probably need to check if the obtained data is a operational node and + then write functions to parse them accordingly. If not then continue below */ + + /* Parse the CBOR structure */ + ret = lydcbor_parse_subtree(lydctx, parent, first_p, parsed, cbor_data); + +cleanup: + if (cbor_data) + { + cbor_decref(&cbor_data); + } + + if (ret) + { + if (lydctx) { + lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); + lydctx = NULL; + } + } + + *lydctx_p = (struct lyd_ctx *)lydctx; + return ret; +} + +#endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file diff --git a/src/parser_cbor.h b/src/parser_cbor.h new file mode 100644 index 000000000..5b6b0dc94 --- /dev/null +++ b/src/parser_cbor.h @@ -0,0 +1,279 @@ +/** + * @file parser_cbor.h + * @author + * @brief CBOR data parser for libyang + * + * Copyright (c) 2020 - 2023 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_PARSER_CBOR_H_ +#define LY_PARSER_CBOR_H_ + +#ifdef ENABLE_CBOR_SUPPORT + +#include +#include +#include + +#include "log.h" +#include "tree_data.h" +#include "parser_internal.h" +#include "set.h" + +struct ly_ctx; +struct ly_in; +struct ly_out; +struct lyd_ctx; +struct lyd_node; +struct ly_set; +struct lysc_ext_instance; +struct lysc_node; + +/** + * @brief CBOR format variants for different encoding schemes + */ +enum lyd_cbor_format +{ + LYD_CBOR_NAMED, /**< CBOR with named identifiers (JSON-like) */ + LYD_CBOR_SID /**< CBOR with Schema Item identifiers (future implementation) */ +}; + +struct lycbor_ctx { + const struct ly_ctx *ctx; /**< libyang context */ + struct ly_in *in; /**< input structure */ + cbor_item_t *cbor_data; /**< parsed CBOR data */ + enum lyd_cbor_format format; /**< CBOR format variant */ + uint32_t parse_opts; /**< parser options */ + uint32_t val_opts; /**< validation options */ +}; + +/** + * @brief Internal context for CBOR YANG data parser. + * + * This structure extends the basic lyd_ctx pattern used throughout libyang + * and provides CBOR-specific parsing state and configuration. + */ +struct lyd_cbor_ctx +{ + const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ + uint32_t parse_opts; /**< various @ref dataparseroptions. */ + uint32_t val_opts; /**< various @ref datavalidationoptions. */ + uint32_t int_opts; /**< internal parser options */ + uint32_t path_len; /**< used bytes in the path buffer */ + char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */ + struct ly_set node_when; /**< set of nodes with "when" conditions */ + struct ly_set node_types; /**< set of nodes with unresolved types */ + struct ly_set meta_types; /**< set of metadata with unresolved types */ + struct ly_set ext_node; /**< set of nodes with extension instances to validate */ + struct ly_set ext_val; /**< set of nested extension data to validate */ + struct lyd_node *op_node; /**< if an operation is being parsed, its node */ + const struct lys_module *val_getnext_ht_mod; + struct ly_ht *val_getnext_ht; + + /* callbacks */ + lyd_ctx_free_clb free; /**< destructor */ + + struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ + + /* CBOR-specific members */ + enum lyd_cbor_format format; /**< CBOR format being parsed */ +}; + +/** + * @brief Create new CBOR context for parsing. + * + * @param[in] ctx libyang context. + * @param[in] in Input handler. + * @param[out] cbor_ctx_p Pointer to store the created CBOR context. + * @return LY_ERR value. + */ +LY_ERR +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p); + +/** + * @brief Free CBOR context. + * + * @param[in] cbor_ctx CBOR context to free. + */ +void +lycbor_ctx_free(struct lycbor_ctx *cbor_ctx); + +/** + * @brief Parse CBOR data into libyang data tree. + * + * This function mirrors the signature and behavior of lyd_parse_json() but handles + * CBOR input instead. It supports both named identifier and SID formats. + * + * @param[in] ctx libyang context. + * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance + * @param[in] parent Parent to connect the parsed nodes to, if any. + * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL. + * @param[in] in Input structure to read from. + * @param[in] parse_opts Options for parser, see @ref dataparseroptions. + * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. + * @param[in] int_opts Internal data parser options. + * @param[out] parsed Set to add all the parsed siblings into. + * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in. + * @param[out] lydctx_p Data parser context to finish validation. + * @return LY_ERR value. + */ +LY_ERR lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p); + +/** + * @brief Parse CBOR data from memory into libyang data tree. + * + * Convenience function for parsing CBOR data directly from memory buffer. + * + * @param[in] ctx libyang context. + * @param[in] data CBOR data to parse. + * @param[in] data_len Length of @p data. + * @param[in] format CBOR format variant (named or SID). + * @param[in] parse_opts Parse options, see @ref dataparseroptions. + * @param[in] val_opts Validation options, see @ref datavalidationoptions. + * @param[out] tree Parsed data tree. + * @return LY_ERR value. + */ +LIBYANG_API_DECL LY_ERR lyd_parse_cbor_data(const struct ly_ctx *ctx, const char *data, size_t data_len, + enum lyd_cbor_format format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **tree); + +/** + * @brief Print libyang data tree as CBOR. + * + * @param[in] root Root node of the data tree to print. + * @param[in] format CBOR format variant to use for output. + * @param[in] out Output structure to write to. + * @param[in] options Print options. + * @return LY_ERR value. + */ +LIBYANG_API_DECL LY_ERR lyd_print_cbor_data(const struct lyd_node *root, enum lyd_cbor_format format, + struct ly_out *out, uint32_t options); + +/* Internal functions (used by parser_data.c and other libyang components) */ + +/** + * @brief Detect CBOR format variant from input data. + * + * @param[in] in Input structure to analyze. + * @param[out] format Detected format. + * @return LY_ERR value. + */ +LY_ERR lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format); + +/** + * @brief Parse a single CBOR value according to schema node. + * + * @param[in] lydctx CBOR parser context. + * @param[in] snode Schema node for the value. + * @param[in] cbor_item CBOR item to parse. + * @param[out] node Created data node. + * @return LY_ERR value. + */ +LY_ERR lydcbor_parse_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const void *cbor_item, struct lyd_node **node); + +/** + * @brief Parse CBOR metadata/attributes. + * + * @param[in] lydctx CBOR parser context. + * @param[in] cbor_item CBOR item containing metadata. + * @param[in,out] node Data node to attach metadata to. + * @return LY_ERR value. + */ +LY_ERR lydcbor_parse_metadata(struct lyd_cbor_ctx *lydctx, const void *cbor_item, struct lyd_node *node); + +/** + * @brief Create a new CBOR parser context. + * + * @param[in] ctx libyang context. + * @param[in] ext Extension instance providing context for the top level element, NULL if none. + * @param[in] parse_opts Parse options, see @ref dataparseroptions. + * @param[in] val_opts Validation options, see @ref datavalidationoptions. + * @param[in] format CBOR format variant (named or SID). + * @param[out] lydctx_p Pointer to the created CBOR parser context. + * @return LY_ERR value. + */ +LY_ERR lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, + uint32_t parse_opts, uint32_t val_opts, enum lyd_cbor_format format, + struct lyd_cbor_ctx **lydctx_p); + + /* Add these declarations to parser_cbor.h or at the top of parser_cbor.c */ + +static LY_ERR +lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed); + + +static LY_ERR +lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed); + +/** + * @brief Parse a CBOR container recursive + */ +static LY_ERR +lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); +static LY_ERR +lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); + +static LY_ERR +lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); + + +static LY_ERR +lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); + +static LY_ERR +lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); +static LY_ERR +lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + struct lyd_node **node, const cbor_item_t *cbor_value); + + +/** + * @brief Get schema node from CBOR node name, following lydjson_get_snode logic. + * + * @param[in] lydctx CBOR parser context. + * @param[in] name Node name. + * @param[in] name_len Length of node name. + * @param[in] parent Data parent node. + * @param[out] snode Schema node found. + * @param[out] ext Extension instance if found. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, + struct lyd_node *parent, const struct lysc_node **snode, + const struct lysc_ext_instance **ext); + + +/** + * @brief Get module prefix from a qualified name. + * + * @param[in] qname Qualified name (prefix:name or just name). + * @param[in] qname_len Length of the qualified name. + * @param[out] prefix Extracted prefix (points into qname, not allocated). + * @param[out] prefix_len Length of the prefix. + * @param[out] name Local name (points into qname, not allocated). + * @param[out] name_len Length of the local name. + * @return LY_SUCCESS on success. + */ +static LY_ERR +lydcbor_parse_qname(const char *qname, size_t qname_len, const char **prefix, size_t *prefix_len, + const char **name, size_t *name_len); + +#endif /* ENABLE_CBOR_SUPPORT */ + +#endif /* LY_PARSER_CBOR_H_ */ \ No newline at end of file diff --git a/src/parser_data.h b/src/parser_data.h index 3941354c5..a41ec77b8 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -261,6 +261,23 @@ LIBYANG_API_DECL LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char uint32_t validate_options, struct lyd_node **tree); /** + * @brief Parse data from a memory buffer with a specified length. + * + * This function parses the provided data buffer of a given length and returns the resulting data tree. + * + * @param[in] ctx libyang context for parsing. + * @param[in] data Pointer to the memory buffer containing the data to parse. + * @param[in] data_len Length of the memory buffer. + * @param[in] format Data format (e.g., XML, JSON, LYD_LYB). + * @param[in] options Parsing options, see @ref dataparseroptions. + * @param[in] ctx_node Optional context node for parsing (can be NULL). + * @param[out] tree Pointer to the resulting data tree (set on success). + * @return LY_ERR value indicating success or error reason. + */ +LIBYANG_API_DECL LY_ERR lyd_parse_data_mem_len(const struct ly_ctx *ctx, const char *data, size_t data_len, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree); + + /** * @brief Parse (and validate) input data as a YANG data tree. * * Wrapper around ::lyd_parse_data() hiding work with the input handler and some obscure options. diff --git a/src/parser_json.c b/src/parser_json.c index 9d09ac6d4..af0ec64b6 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -4,7 +4,7 @@ * @author Michal Vasko * @brief JSON data parser for libyang * - * Copyright (c) 2020 - 2026 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2023 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. @@ -38,9 +38,6 @@ #include "tree_schema_internal.h" #include "validation.h" -static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, - struct ly_set *parsed); - /** * @brief Free the JSON data parser context. * @@ -177,7 +174,7 @@ lydjson_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t module_name = onode->name.prefix; break; } - node = node->parent; + node = lyd_parent(node); } *prefix_p = module_name; @@ -256,37 +253,14 @@ static LY_ERR lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name, size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) { - LY_ERR r; + LY_ERR ret = LY_SUCCESS, r; struct lys_module *mod = NULL; - const struct lysc_node *sparent; uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; *snode = NULL; - if (ext) { - *ext = NULL; - } + *ext = NULL; - /* try to find parent schema node */ - if (parent && parent->schema && !(parent->schema->nodetype & LYD_NODE_ANY)) { - /* use only a schema parent */ - sparent = parent->schema; - } else { - sparent = NULL; - } - if (!prefix_len) { - prefix = NULL; - } - r = lys_find_child_node(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, sparent, NULL, prefix, prefix_len, - LY_VALUE_JSON, NULL, name, name_len, getnext_opts, snode, ext); - LY_CHECK_RET(r && (r != LY_ENOT), r); - - if (!r) { - /* check that schema node is valid and can be used */ - LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode)); - return LY_SUCCESS; - } - - /* generate error, find the module */ + /* get the element module, prefer parent context because of extensions */ if (prefix_len) { mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, prefix, prefix_len); } else if (parent) { @@ -294,38 +268,79 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref mod = parent->schema->module; } } else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) { - LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_SYNTAX_JSON, - "Top-level JSON object member \"%.*s\" must be namespace-qualified.", + LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Top-level JSON object member \"%.*s\" must be namespace-qualified.", (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name); - return LY_EVALID; - } - if (!(lydctx->parse_opts & LYD_PARSE_STRICT)) { - return LY_SUCCESS; + ret = LY_EVALID; + goto cleanup; } - if (!mod) { + /* check for extension data */ + r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext); + if (r != LY_ENOT) { + /* success or error */ + ret = r; + goto cleanup; + } + /* unknown module */ - LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", - (int)prefix_len, prefix); - return LY_EVALID; + if (lydctx->parse_opts & LYD_PARSE_STRICT) { + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix); + ret = LY_EVALID; + goto cleanup; + } } - /* unknown node name */ - if (sparent) { - LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", - (int)name_len, name, sparent->name); - } else { - LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", - (int)name_len, name, mod->name); + /* get the schema node */ + if (mod && (!parent || parent->schema)) { + if (!parent && lydctx->ext) { + *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts); + } else { + *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts); + } + if (!*snode) { + /* check for extension data */ + r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext); + if (r != LY_ENOT) { + /* success or error */ + ret = r; + goto cleanup; + } + + /* unknown data node */ + if (lydctx->parse_opts & LYD_PARSE_STRICT) { + if (parent) { + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", + (int)name_len, name, LYD_NAME(parent)); + } else if (lydctx->ext) { + if (lydctx->ext->argument) { + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, + "Node \"%.*s\" not found in the \"%s\" %s extension instance.", + (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name); + } else { + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.", + (int)name_len, name, lydctx->ext->def->name); + } + } else { + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", + (int)name_len, name, mod->name); + } + ret = LY_EVALID; + goto cleanup; + } + } else { + /* check that schema node is valid and can be used */ + ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode); + } } - return LY_EVALID; + +cleanup: + return ret; } /** * @brief Get the hint for the data type parsers according to the current JSON parser context. * * @param[in] lydctx JSON data parser context. - * @param[in] snode Schema node for logging. * @param[in,out] status Pointer to the current context status, * in some circumstances the function manipulates with the context so the status is updated. * @param[out] type_hint_p Pointer to the variable to store the result. @@ -333,8 +348,7 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref * @return LY_EINVAL in case of invalid context status not referring to a value. */ static LY_ERR -lydjson_value_type_hint(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, enum LYJSON_PARSER_STATUS *status_p, - uint32_t *type_hint_p) +lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p) { struct lyjson_ctx *jsonctx = lydctx->jsonctx; @@ -344,28 +358,16 @@ lydjson_value_type_hint(struct lyd_json_ctx *lydctx, const struct lysc_node *sno /* only [null] */ LY_CHECK_RET(lyjson_ctx_next(jsonctx, status_p)); if (*status_p != LYJSON_NULL) { - if (snode) { - LOG_LOCSET(snode); - } - LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, + LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected JSON name/value or special name/[null], but input data contains name/[%s].", lyjson_token2str(*status_p)); - if (snode) { - LOG_LOCBACK(1); - } return LY_EINVAL; } LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL)); if (lyjson_ctx_status(jsonctx) != LYJSON_ARRAY_CLOSED) { - if (snode) { - LOG_LOCSET(snode); - } - LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.", + LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.", lyjson_token2str(*status_p)); - if (snode) { - LOG_LOCBACK(1); - } return LY_EINVAL; } @@ -379,13 +381,7 @@ lydjson_value_type_hint(struct lyd_json_ctx *lydctx, const struct lysc_node *sno } else if (*status_p == LYJSON_NULL) { *type_hint_p = 0; } else { - if (snode) { - LOG_LOCSET(snode); - } - LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p)); - if (snode) { - LOG_LOCBACK(1); - } + LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p)); return LY_EINVAL; } @@ -462,10 +458,9 @@ lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list) goto cleanup; } - rc = lydjson_value_type_hint(lydctx, snode, &status, &hints); + rc = lydjson_value_type_hint(lydctx, &status, &hints); LY_CHECK_GOTO(rc, cleanup); - rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len * 8, LY_VALUE_JSON, NULL, - hints); + rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints); LY_CHECK_GOTO(rc, cleanup); /* key with a valid value, remove from the set */ @@ -532,18 +527,15 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno switch (snode->nodetype) { case LYS_LEAFLIST: case LYS_LEAF: - prev_lo = ly_temp_log_options(&temp_lo); - /* value may not be valid in which case we parse it as an opaque node */ - if (lydjson_value_type_hint(lydctx, snode, &status, type_hint_p)) { - ret = LY_ENOT; + if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) { + break; } - if (!ret && ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len * 8, LY_VALUE_JSON, NULL, - *type_hint_p)) { + prev_lo = ly_temp_log_options(&temp_lo); + if (ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, *type_hint_p)) { ret = LY_ENOT; } - ly_temp_log_options(prev_lo); break; case LYS_LIST: @@ -555,7 +547,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno break; } } else if (snode->nodetype & LYD_NODE_TERM) { - ret = lydjson_value_type_hint(lydctx, snode, &status, type_hint_p); + ret = lydjson_value_type_hint(lydctx, &status, type_hint_p); } /* restore parser */ @@ -580,8 +572,10 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) { LY_ERR ret = LY_SUCCESS; struct lyd_node *node, *attr, *next, *meta_iter; + struct lysc_ext_instance *ext; uint64_t instance = 0; const char *prev = NULL; + uint32_t log_location_items = 0; /* finish linking metadata */ LY_LIST_FOR_SAFE(*first_p, next, attr) { @@ -597,6 +591,9 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) continue; } + LOG_LOCSET(NULL, attr); + log_location_items++; + if (prev != meta_container->name.name) { /* metas' names are stored in dictionary, so checking pointers must works */ lydict_remove(lydctx->jsonctx->ctx, prev); @@ -615,7 +612,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) } if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) { - LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", + LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", ((struct lyd_node_opaq *)node)->name.name); ret = LY_EVALID; goto cleanup; @@ -646,7 +643,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) lydjson_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len, &prefix, &prefix_len, &is_attr); assert(is_attr); - lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, (*first_p)->parent, &snode, NULL); + lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext); if (snode != node->schema) { continue; @@ -665,18 +662,18 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) mod = ly_ctx_get_module_implemented(lydctx->jsonctx->ctx, meta->name.prefix); if (mod) { - ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, meta->name.name, - strlen(meta->name.name), meta->value, ly_strlen(meta->value) * 8, NULL, LY_VALUE_JSON, - NULL, meta->hints, node->schema, node); + ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, + meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value), + NULL, LY_VALUE_JSON, NULL, meta->hints, node->schema); LY_CHECK_GOTO(ret, cleanup); } else if (lydctx->parse_opts & LYD_PARSE_STRICT) { if (meta->name.prefix) { - LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", meta->name.name); } else { - LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", meta->name.name); } ret = LY_EVALID; @@ -685,7 +682,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) } /* add/correct flags */ - ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL); + ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext); LY_CHECK_GOTO(ret, cleanup); break; } @@ -694,11 +691,11 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) if (match != instance) { /* there is no corresponding data node for the metadata */ if (instance > 1) { - LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu64 " to be coupled with %s metadata.", instance, meta_container->name.name); } else { - LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, "Missing JSON data instance to be coupled with %s metadata.", + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance to be coupled with %s metadata.", meta_container->name.name); } ret = LY_EVALID; @@ -709,10 +706,15 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) } lyd_free_tree(attr); } + + LOG_LOCBACK(0, log_location_items); + log_location_items = 0; } cleanup: lydict_remove(lydctx->jsonctx->ctx, prev); + + LOG_LOCBACK(0, log_location_items); return ret; } @@ -720,12 +722,15 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) * @brief Parse a metadata member/attribute. * * @param[in] lydctx JSON data parser context. - * @param[in] node Parent node. + * @param[in] snode Schema node of the metadata parent. + * @param[in] node Parent node in case the metadata is not forward-referencing (only LYD_NODE_TERM) + * so the data node does not exists. In such a case the metadata is stored in the context for the later + * processing by lydjson_metadata_finish(). * @return LY_SUCCESS on success * @return Various LY_ERR values in case of failure. */ static LY_ERR -lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) +lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node) { LY_ERR rc = LY_SUCCESS, r; enum LYJSON_PARSER_STATUS status; @@ -741,7 +746,12 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) uint32_t instance = 0, val_hints; uint16_t nodetype; - nodetype = node->schema ? node->schema->nodetype : LYS_CONTAINER; + assert(snode || node); + + nodetype = snode ? snode->nodetype : LYS_CONTAINER; + if (snode) { + LOG_LOCSET(snode, NULL); + } /* move to the second item in the name/X pair */ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup); @@ -764,10 +774,8 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_NULL), representation_error); if (!node || (node->schema != prev->schema)) { - LOG_LOCSET(prev->schema); - LOGVAL(lydctx->jsonctx->ctx, NULL, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu32 + LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); - LOG_LOCBACK(1); rc = LY_EVALID; goto cleanup; } @@ -813,16 +821,16 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &dynamic_prefname); if (!name_len) { - LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name); + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name); rc = LY_EVALID; goto cleanup; } else if (!prefix_len) { - LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".", + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".", (int)lydctx->jsonctx->value_len, lydctx->jsonctx->value); rc = LY_EVALID; goto cleanup; } else if (is_attr) { - LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"", + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"", (int)lydctx->jsonctx->value_len, lydctx->jsonctx->value); rc = LY_EVALID; goto cleanup; @@ -832,8 +840,7 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len); if (!mod) { if (lydctx->parse_opts & LYD_PARSE_STRICT) { - LOGVAL(ctx, prev, LYVE_REFERENCE, - "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", + LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", (int)prefix_len, prefix, (int)name_len, name); rc = LY_EVALID; goto cleanup; @@ -852,13 +859,12 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup); /* get value hints */ - LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, node->schema, &status, &val_hints), cleanup); + LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup); if (node->schema) { /* create metadata */ - rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, - lydctx->jsonctx->value, lydctx->jsonctx->value_len * 8, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, - NULL, val_hints, node->schema, node); + rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value, + lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, val_hints, node->schema); LY_CHECK_GOTO(rc, cleanup); /* add/correct flags */ @@ -897,7 +903,7 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) goto cleanup; representation_error: - LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, + LOGVAL(ctx, LYVE_SYNTAX_JSON, "The attribute(s) of %s \"%s\" is expected to be represented as JSON %s, but input data contains @%s/%s.", lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected, lyjson_token2str(status), in_parent ? "" : "name"); @@ -911,9 +917,46 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) } } free(dynamic_prefname); + LOG_LOCBACK(snode ? 1 : 0, 0); return rc; } +/** + * @brief Eat the node pointed by @p node_p by inserting it into @p parent and maintain the @p first_p pointing + * to the first child node. + * + * @param[in] parent Parent node to insert to, can be NULL in case of top-level (or provided first_p). + * @param[in,out] first_p Pointer to the first sibling node in case of top-level. + * @param[in,out] node_p pointer to the new node to insert, after the insert is done, pointer is set to NULL. + * @param[in] last If set, always insert at the end. + * @param[in] ext Extension instance of @p node_p, if any. + */ +static void +lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last, + struct lysc_ext_instance *ext) +{ + if (!*node_p) { + return; + } + + /* insert, keep first pointer correct */ + if (ext) { + lyplg_ext_insert(parent, *node_p); + } else { + lyd_insert_node(parent, first_p, *node_p, last); + } + if (first_p) { + if (parent) { + *first_p = lyd_child(parent); + } else { + while ((*first_p)->prev->next) { + *first_p = (*first_p)->prev; + } + } + } + *node_p = NULL; +} + /** * @brief Wrapper for ::lyd_create_opaq(). * @@ -945,7 +988,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l dynamic = lydctx->jsonctx->dynamic; lydctx->jsonctx->dynamic = 0; - LY_CHECK_RET(lydjson_value_type_hint(lydctx, NULL, status_inner_p, &type_hint)); + LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint)); } /* get the module name */ @@ -966,6 +1009,9 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l return ret; } +static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, + struct ly_set *parsed); + /** * @brief Parse opaq node from the input. * @@ -983,30 +1029,29 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l * @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone * context status of the array content. Otherwise, it is supposed to be the same as @p status_p. * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not. - * @param[out] node Pointer to the created opaq node. + * @param[out] node_p Pointer to the created opaq node. * @return LY_ERR value. */ static LY_ERR lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p, - struct lyd_node **first_p, struct lyd_node **node) + struct lyd_node **first_p, struct lyd_node **node_p) { LY_ERR ret = LY_SUCCESS; - LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node), cleanup); + LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup); - /* insert */ - ret = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); - LY_CHECK_GOTO(ret, cleanup); + assert(*node_p); + LOG_LOCSET(NULL, *node_p); if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) { /* special array null value */ - ((struct lyd_node_opaq *)*node)->hints |= LYD_VALHINT_EMPTY; + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY; /* must be the only item */ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup); if (*status_inner_p != LYJSON_ARRAY_CLOSED) { - LOGVAL(lydctx->jsonctx->ctx, *node, LYVE_SYNTAX, "Array \"null\" member with another member."); + LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member."); ret = LY_EVALID; goto cleanup; } @@ -1018,16 +1063,16 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le /* process another instance of the same node */ if (*status_inner_p == LYJSON_OBJECT) { /* array with objects, list */ - ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST; + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST; /* but first process children of the object in the array */ do { - LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL), cleanup); + LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, (*node_p), NULL), cleanup); *status_inner_p = lyjson_ctx_status(lydctx->jsonctx); } while (*status_inner_p == LYJSON_OBJECT_NEXT); } else { /* array with values, leaf-list */ - ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST; + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST; } LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup); @@ -1038,28 +1083,35 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le /* continue with the next instance */ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup); - LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node), - cleanup); + assert(*node_p); + lydjson_maintain_children(parent, first_p, node_p, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); + + LOG_LOCBACK(0, 1); + + LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup); - /* insert */ - ret = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); - LY_CHECK_GOTO(ret, cleanup); + assert(*node_p); + LOG_LOCSET(NULL, *node_p); } if (*status_p == LYJSON_OBJECT) { - ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_CONTAINER; + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_CONTAINER; /* process children */ do { - LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL), cleanup); + LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup); *status_p = lyjson_ctx_status(lydctx->jsonctx); } while (*status_p == LYJSON_OBJECT_NEXT); } finish: /* finish linking metadata */ - ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node)); + ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p)); cleanup: + if (*node_p) { + LOG_LOCBACK(0, 1); + } return ret; } @@ -1080,13 +1132,13 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le * @param[in,out] status_p Pointer to the current status of the parser context, * since the function manipulates with the context and process the input, the status can be updated. * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not. - * @param[out] node Pointer to the created opaq node. + * @param[out] node_p Pointer to the created opaq node. * @return LY_ERR value. */ static LY_ERR lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, - struct lyd_node **first_p, struct lyd_node **node) + struct lyd_node **first_p, struct lyd_node **node_p) { enum LYJSON_PARSER_STATUS status_inner = 0; @@ -1107,7 +1159,7 @@ lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_ /* parse opaq node from the input */ LY_CHECK_RET(lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, &status_inner, - first_p, node)); + first_p, node_p)); return LY_SUCCESS; } @@ -1136,8 +1188,8 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, enum LYJSON_PARSER_STATUS *status_p, struct lyd_node **first_p, struct lyd_node **node_p) { LY_ERR r; - const char *opaq_name, *mod_name, *attr_mod = NULL; - size_t opaq_name_len, attr_mod_len = 0; + const char *opaq_name, *mod_name, *attr_mod; + size_t opaq_name_len, attr_mod_len; if (!attr_node) { /* learn the attribute module name */ @@ -1199,7 +1251,7 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, lydctx->parse_opts = prev_opts; LY_CHECK_RET(r); } else { - LY_CHECK_RET(lydjson_meta_attr(lydctx, attr_node)); + LY_CHECK_RET(lydjson_meta_attr(lydctx, snode, attr_node)); } return LY_SUCCESS; @@ -1212,8 +1264,6 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, * as before calling, despite it is necessary to process input data for checking. * @param[in] snode Schema node corresponding to the member currently being processed in the context. * @param[in] ext Extension instance of @p snode, if any. - * @param[in,out] parent Parent node, if any. - * @param[in,out] first_p First top-level node, is updated. * @param[in,out] status JSON parser status, is updated. * @param[out] node Parsed data (or opaque) node. * @return LY_SUCCESS if a node was successfully parsed, @@ -1221,14 +1271,16 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, * @return LY_ERR on other errors. */ static LY_ERR -lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext, - struct lyd_node *parent, struct lyd_node **first_p, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) +lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, + enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts; struct ly_in in_start; char *val = NULL; const char *end; + struct lyd_node *child = NULL; + ly_bool log_node = 0; assert(snode->nodetype & LYD_NODE_ANY); @@ -1246,9 +1298,36 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co /* create any node */ switch (*status) { case LYJSON_OBJECT: - /* create empty node */ - r = lyd_create_any(snode, NULL, NULL, 0, 1, 0, node); + /* create node */ + r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + assert(*node); + LOG_LOCSET(NULL, *node); + log_node = 1; + + /* parse any data tree with correct options, first backup the current options and then make the parser + * process data as opaq nodes */ + lydctx->parse_opts &= ~LYD_PARSE_STRICT; + lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0); + lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS; + lydctx->any_schema = snode; + + /* process the anydata content */ + do { + r = lydjson_subtree_r(lydctx, NULL, &child, NULL); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + *status = lyjson_ctx_status(lydctx->jsonctx); + } while (*status == LYJSON_OBJECT_NEXT); + + /* finish linking metadata */ + r = lydjson_metadata_finish(lydctx, &child); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + /* assign the data tree */ + ((struct lyd_node_any *)*node)->value.tree = child; + child = NULL; break; case LYJSON_ARRAY: /* skip until the array end */ @@ -1267,21 +1346,20 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co rc = LY_EMEM; goto cleanup; } - r = lyd_create_any(snode, NULL, val, LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST, 1, 0, node); + r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); val = NULL; break; case LYJSON_STRING: /* string value */ if (lydctx->jsonctx->dynamic) { - LY_CHECK_GOTO(rc = lyd_create_any(snode, NULL, lydctx->jsonctx->value, - LYD_VALHINT_STRING | LYD_VALHINT_NUM64, 1, 0, node), cleanup); + LY_CHECK_GOTO(rc = lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node), cleanup); lydctx->jsonctx->dynamic = 0; } else { val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len); LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup); - r = lyd_create_any(snode, NULL, val, LYD_VALHINT_STRING | LYD_VALHINT_NUM64, 1, 0, node); + r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); val = NULL; } @@ -1294,13 +1372,13 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len); LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup); - r = lyd_create_any(snode, NULL, val, (*status == LYJSON_NUMBER) ? LYD_VALHINT_DECNUM : LYD_VALHINT_BOOLEAN, 1, 0, node); + r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); val = NULL; break; case LYJSON_NULL: /* no value */ - r = lyd_create_any(snode, NULL, NULL, LYD_VALHINT_EMPTY, 1, 0, node); + r = lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); break; default: @@ -1309,45 +1387,15 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co goto cleanup; } - /* insert, needs LYD_EXT flag */ - if (ext) { - (*node)->flags |= LYD_EXT; - } - r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - - /* parse descendants */ - if (*status == LYJSON_OBJECT) { - if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) { - /* explicit strict data parsing */ - lydctx->parse_opts |= LYD_PARSE_STRICT; - } else { - /* make the parser process data as opaq nodes */ - lydctx->parse_opts &= ~LYD_PARSE_STRICT; - lydctx->parse_opts |= LYD_PARSE_OPAQ; - } - lydctx->parse_opts |= (ext ? LYD_PARSE_ONLY : 0); - lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS; - lydctx->any_schema = snode; - - /* process the anydata content */ - do { - r = lydjson_subtree_r(lydctx, *node, &((struct lyd_node_any *)*node)->child, NULL); - LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - - *status = lyjson_ctx_status(lydctx->jsonctx); - } while (*status == LYJSON_OBJECT_NEXT); - - /* finish linking metadata */ - r = lydjson_metadata_finish(lydctx, &((struct lyd_node_any *)*node)->child); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - } - cleanup: + if (log_node) { + LOG_LOCBACK(0, 1); + } lydctx->parse_opts = prev_parse_opts; lydctx->int_opts = prev_int_opts; lydctx->any_schema = NULL; free(val); + lyd_free_tree(child); return rc; } @@ -1357,8 +1405,6 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co * @param[in] lydctx JSON data parser context. * @param[in] snode Schema node corresponding to the member currently being processed in the context. * @param[in] ext Extension instance of @p snode, if any. - * @param[in,out] parent Parent node, if any. - * @param[in,out] first_p First top-level node, is updated. * @param[in,out] status JSON parser status, is updated. * @param[out] node Parsed data (or opaque) node. * @return LY_SUCCESS if a node was successfully parsed, @@ -1366,8 +1412,8 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, co * @return LY_ERR on other errors. */ static LY_ERR -lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext, - struct lyd_node *parent, struct lyd_node **first_p, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) +lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, + enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t prev_parse_opts = lydctx->parse_opts; @@ -1377,12 +1423,8 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node /* create inner node */ LY_CHECK_RET(lyd_create_inner(snode, node)); - /* insert, needs LYD_EXT flag */ - if (ext) { - (*node)->flags |= LYD_EXT; - } - r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + /* use it for logging */ + LOG_LOCSET(NULL, *node); if (ext) { /* only parse these extension data and validate afterwards */ @@ -1394,10 +1436,6 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node r = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - /* insert again, node may be a list that had its keys missing */ - r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - *status = lyjson_ctx_status(lydctx->jsonctx); } while (*status == LYJSON_OBJECT_NEXT); @@ -1419,8 +1457,11 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node cleanup: lydctx->parse_opts = prev_parse_opts; - if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { - lyd_parser_node_free(first_p, node); + LOG_LOCBACK(0, 1); + if (!(*node)->hash) { + /* list without keys is unusable */ + lyd_free_tree(*node); + *node = NULL; } return rc; } @@ -1446,12 +1487,14 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node */ static LY_ERR lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, - const struct lysc_node *snode, const struct lysc_ext_instance *ext, const char *name, size_t name_len, + const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len, const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t type_hints = 0; + LOG_LOCSET(snode, NULL); + r = lydjson_data_check_opaq(lydctx, snode, &type_hints); if (r == LY_SUCCESS) { assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY)); @@ -1466,17 +1509,10 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str } /* create terminal node */ - r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, parent, lydctx->jsonctx->value, - lydctx->jsonctx->value_len * 8, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node); + r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value, + lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - /* insert, needs LYD_EXT flag */ - if (ext) { - (*node)->flags |= LYD_EXT; - } - r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - /* move JSON parser */ if (*status == LYJSON_ARRAY) { /* only [null], 2 more moves are needed */ @@ -1490,11 +1526,11 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str } } else if (snode->nodetype & LYD_NODE_INNER) { /* create inner node */ - r = lydjson_parse_instance_inner(lydctx, snode, ext, parent, first_p, status, node); + r = lydjson_parse_instance_inner(lydctx, snode, ext, status, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } else { /* create any node */ - r = lydjson_parse_any(lydctx, snode, ext, parent, first_p, status, node); + r = lydjson_parse_any(lydctx, snode, ext, status, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } LY_CHECK_GOTO(!*node, cleanup); @@ -1502,6 +1538,12 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str /* add/correct flags */ r = lyd_parser_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) { + /* store for ext instance node validation, if needed */ + r = lyd_validate_node_ext(*node, &lydctx->ext_node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } } else if (r == LY_ENOT) { /* parse it again as an opaq node */ r = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node); @@ -1519,6 +1561,7 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str } cleanup: + LOG_LOCBACK(1, 0); return rc; } @@ -1538,7 +1581,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx); const char *name, *prefix = NULL, *expected = NULL; size_t name_len, prefix_len = 0; - ly_bool is_meta = 0; + ly_bool is_meta = 0, parse_subtree; const struct lysc_node *snode = NULL; struct lysc_ext_instance *ext = NULL; struct lyd_node *node = NULL, *attr_node = NULL; @@ -1548,6 +1591,10 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l assert(parent || first_p); assert((status == LYJSON_OBJECT) || (status == LYJSON_OBJECT_NEXT)); + parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0; + /* all descendants should be parsed */ + lydctx->parse_opts &= ~LYD_PARSE_SUBTREE; + r = lyjson_ctx_next(lydctx->jsonctx, &status); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (status == LYJSON_OBJECT_CLOSED) { @@ -1567,8 +1614,8 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (status != LYJSON_STRING) { - LOGVAL(lydctx->jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", - lyjson_token2str(LYJSON_STRING), lyjson_token2str(status)); + LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING), + lyjson_token2str(status)); rc = LY_EVALID; goto cleanup; } @@ -1578,10 +1625,6 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l lydctx->jsonctx->value, lydctx->jsonctx->value_len, NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, &node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - /* insert */ - r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, node); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - /* validate the value */ r = lyd_parser_notif_eventtime_validate(node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); @@ -1613,7 +1656,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l if (is_meta) { /* parse as metadata */ if (!name_len && !prefix_len && !parent) { - LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1635,7 +1678,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l /* opaq node cannot have an empty string as the name. */ if (name_len == 0) { - LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_SYNTAX_JSON, "JSON object member name cannot be a zero-length string."); + LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "JSON object member name cannot be a zero-length string."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -1705,6 +1748,9 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l } LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + lydjson_maintain_children(parent, first_p, &node, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + /* move after the item(s) */ r = lyjson_ctx_next(lydctx->jsonctx, &status); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); @@ -1740,15 +1786,21 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l ly_set_add(parsed, node, 1, NULL); } - /* move after the item(s) */ - r = lyjson_ctx_next(lydctx->jsonctx, &status); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + /* finally connect the parsed node, is zeroed */ + lydjson_maintain_children(parent, first_p, &node, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + + if (!parse_subtree) { + /* move after the item(s) */ + r = lyjson_ctx_next(lydctx->jsonctx, &status); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } /* success */ goto cleanup; representation_error: - LOGVAL(ctx, parent, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as name/%s.", + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as name/%s.", expected, lys_nodetype2str(snode->nodetype), snode->name, lyjson_token2str(status)); rc = LY_EVALID; if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) { @@ -1760,9 +1812,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l cleanup: free(value); - if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { - lyd_parser_node_free(first_p, &node); - } + lyd_free_tree(node); return rc; } @@ -1770,17 +1820,15 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l * @brief Common start of JSON parser processing different types of the input data. * * @param[in] ctx libyang context - * @param[in] schema Schema node of the potential bare value to check. * @param[in] in Input structure. * @param[in] parse_opts Options for parser, see @ref dataparseroptions. * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. - * @param[out] status_p Initial status of the input structure. * @param[out] lydctx_p Data parser context to finish validation. * @return LY_ERR value. */ static LY_ERR -lyd_parse_json_init(const struct ly_ctx *ctx, const struct lysc_node *schema, struct ly_in *in, uint32_t parse_opts, - uint32_t val_opts, enum LYJSON_PARSER_STATUS *status_p, struct lyd_json_ctx **lydctx_p) +lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, + struct lyd_json_ctx **lydctx_p) { LY_ERR ret = LY_SUCCESS; struct lyd_json_ctx *lydctx; @@ -1799,169 +1847,55 @@ lyd_parse_json_init(const struct ly_ctx *ctx, const struct lysc_node *schema, st status = lyjson_ctx_status(lydctx->jsonctx); /* parse_opts & LYD_PARSE_SUBTREE not implemented */ - /* there are two options: either we want to parse a bare JSON value or a JSON object - * node of the bare JSON value has to have a schema, otherwise we do not know where to put the value - * the only types of nodes that can take on a value are a leaf (number, string or bool) and a leaf-list (array) */ - if (schema && - (((status == LYJSON_ARRAY) && (schema->nodetype & LYS_LEAFLIST)) || - (((status == LYJSON_NUMBER) || (status == LYJSON_STRING) || (status == LYJSON_FALSE) || - (status == LYJSON_TRUE) || (status == LYJSON_NULL) || (status == LYJSON_ARRAY)) && (schema->nodetype & LYS_LEAF)))) { - /* bare value (bare anydata 'value = object' is not supported) */ - } else if (status == LYJSON_OBJECT) { - /* JSON object */ - } else { - /* expecting top-level object or bare value */ - LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Expected top-level JSON object or correct bare value, but %s found.", - lyjson_token2str(status)); + if (status != LYJSON_OBJECT) { + /* expecting top-level object */ + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(status)); *lydctx_p = NULL; lyd_json_ctx_free((struct lyd_ctx *)lydctx); return LY_EVALID; } *lydctx_p = lydctx; - if (status_p) { - *status_p = status; - } return LY_SUCCESS; } -/** - * @brief Parse a bare JSON value. - * - * @param[in] lydctx Data parser context. - * @param[in,out] status Current status of the data parser context. - * @param[in] parent Parent to connect the parsed nodes to, if any. - * @param[in] schema Optional schema node of the parsed node (mandatory when parsing JSON value fragment). - * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL. - * @return LY_ERR value. - */ -static LY_ERR -lyd_parse_json_bare_value(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status, struct lyd_node *parent, - const struct lysc_node *schema, struct lyd_node **first_p) -{ - LY_ERR r, rc = LY_SUCCESS; - const struct ly_ctx *ctx = lydctx->jsonctx->ctx; - struct lyd_node *node = NULL; - const char *expected = NULL; - - assert(schema); - - /* this branch partly copies the behavior of lydjson_subtree_r() */ - - /* set expected representation */ - switch (schema->nodetype) { - case LYS_LEAFLIST: - expected = "array of values"; - break; - case LYS_LEAF: - if (*status == LYJSON_ARRAY) { - expected = "[null]"; - } else { - expected = "value"; - } - break; - } - - /* check the representation according to the nodetype and then continue with the content */ - /* for now object values are not supported (anydata) */ - /* for now extensions not supported */ - switch (schema->nodetype) { - case LYS_LEAFLIST: - LY_CHECK_GOTO(*status != LYJSON_ARRAY, representation_error); - - /* process all the values/objects */ - do { - /* move into array/next value */ - r = lyjson_ctx_next(lydctx->jsonctx, status); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (*status == LYJSON_ARRAY_CLOSED) { - /* empty array, fine... */ - break; - } - - r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name), - NULL, 0, status, &node); - if (r == LY_ENOT) { - goto representation_error; - } - LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - - /* move after the item(s) */ - r = lyjson_ctx_next(lydctx->jsonctx, status); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - } while (*status == LYJSON_ARRAY_NEXT); - - break; - case LYS_LEAF: - /* process the value/object */ - r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name), - NULL, 0, status, &node); - if (r == LY_ENOT) { - goto representation_error; - } - LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - - break; - } - - goto cleanup; - -representation_error: - LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as %s.", - expected, lys_nodetype2str(schema->nodetype), schema->name, lyjson_token2str(*status)); - rc = LY_EVALID; - -cleanup: - if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { - lyd_parser_node_free(first_p, &node); - } - return rc; -} - LY_ERR -lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lysc_node *schema, +lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, - struct ly_set *parsed, struct lyd_ctx **lydctx_p) + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) { LY_ERR r, rc = LY_SUCCESS; struct lyd_json_ctx *lydctx = NULL; - enum LYJSON_PARSER_STATUS status = LYJSON_ERROR; + enum LYJSON_PARSER_STATUS status; - rc = lyd_parse_json_init(ctx, schema, in, parse_opts, val_opts, &status, &lydctx); + rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx); LY_CHECK_GOTO(rc, cleanup); lydctx->int_opts = int_opts; + lydctx->ext = ext; /* find the operation node if it exists already */ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); - if (status != LYJSON_OBJECT) { - /* parse bare JSON value */ - r = lyd_parse_json_bare_value(lydctx, &status, parent, schema, first_p); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - } else { - /* parse JSON object */ - - /* read subtree(s) */ - do { - r = lydjson_subtree_r(lydctx, parent, first_p, parsed); - LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + /* read subtree(s) */ + do { + r = lydjson_subtree_r(lydctx, parent, first_p, parsed); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - status = lyjson_ctx_status(lydctx->jsonctx); + status = lyjson_ctx_status(lydctx->jsonctx); - if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) { - break; - } - } while (status == LYJSON_OBJECT_NEXT); - } + if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) { + break; + } + } while (status == LYJSON_OBJECT_NEXT); if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) { - LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); + LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { - LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); + LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -1970,6 +1904,19 @@ lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, const struct l r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + if (parse_opts & LYD_PARSE_SUBTREE) { + /* check for a sibling object */ + assert(subtree_sibling); + if (status == LYJSON_OBJECT_NEXT) { + *subtree_sibling = 1; + + /* move to the next object */ + ly_in_skip(lydctx->jsonctx->in, 1); + } else { + *subtree_sibling = 0; + } + } + cleanup: /* there should be no unresolved types stored */ assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count && @@ -1978,16 +1925,11 @@ lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, const struct l if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { lyd_json_ctx_free((struct lyd_ctx *)lydctx); } else { + *lydctx_p = (struct lyd_ctx *)lydctx; + /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */ lyjson_ctx_free(lydctx->jsonctx); lydctx->jsonctx = NULL; - - /* set optional lydctx pointer, otherwise free */ - if (lydctx_p) { - *lydctx_p = (struct lyd_ctx *)lydctx; - } else { - lyd_json_ctx_free((struct lyd_ctx *)lydctx); - } } return rc; } @@ -2019,7 +1961,7 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul r = lyjson_ctx_next(jsonctx, &status); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (status == LYJSON_OBJECT_CLOSED) { - LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Empty JSON object."); + LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON object."); rc = LY_EVALID; goto cleanup; } @@ -2028,16 +1970,15 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul assert(status == LYJSON_OBJECT_NAME); lydjson_parse_name(jsonctx->value, jsonctx->value_len, &nam, &nam_len, &prefix, &prefix_len, &is_meta); if (is_meta) { - LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected metadata."); + LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected metadata."); rc = LY_EVALID; goto cleanup; } else if (module && ly_strncmp(module, prefix, prefix_len)) { - LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, - module); + LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, module); rc = LY_EVALID; goto cleanup; } else if (ly_strncmp(name, nam, nam_len)) { - LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name); + LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name); rc = LY_EVALID; goto cleanup; } @@ -2059,9 +2000,9 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul } LY_ERR -lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, - uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct lyd_node **envp, struct ly_set *parsed, - struct lyd_ctx **lydctx_p) +lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, + struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p) { LY_ERR rc = LY_SUCCESS, r; struct lyd_json_ctx *lydctx = NULL; @@ -2074,10 +2015,12 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) || (data_type == LYD_TYPE_REPLY_RESTCONF)); + assert(!(parse_opts & LYD_PARSE_SUBTREE)); /* init context */ - rc = lyd_parse_json_init(ctx, NULL, in, parse_opts, val_opts, NULL, &lydctx); + rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx); LY_CHECK_GOTO(rc, cleanup); + lydctx->ext = ext; switch (data_type) { case LYD_TYPE_RPC_RESTCONF: @@ -2131,7 +2074,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc /* close all opened elements */ for (i = 0; i < close_elem; ++i) { if (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED) { - LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED), + LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED), lyjson_token2str(lyjson_ctx_status(lydctx->jsonctx))); rc = LY_EVALID; goto cleanup; @@ -2142,7 +2085,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc } if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { - LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); + LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -2150,7 +2093,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struc /* parse as a child of the envelope */ node = (*first_p)->prev; if (node->schema) { - LOGVAL(ctx, NULL, LYVE_DATA, "Missing notification \"eventTime\" node."); + LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } else { diff --git a/src/printer_data.c b/src/printer_data.c index 174131b07..797b95371 100644 --- a/src/printer_data.c +++ b/src/printer_data.c @@ -38,6 +38,9 @@ lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, u case LYD_LYB: ret = lyb_print_data(out, root, options); break; + case LYD_CBOR: + // ret = cbor_print_data(out, root, options); + break; case LYD_UNKNOWN: LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "Unknown data output format."); ret = LY_EINVAL; diff --git a/src/tree.h b/src/tree.h index 283cea768..9e2b7fa8d 100644 --- a/src/tree.h +++ b/src/tree.h @@ -237,6 +237,7 @@ typedef enum { LY_VALUE_SCHEMA_RESOLVED, /**< resolved YANG schema value, prefixes map to module structures directly */ LY_VALUE_XML, /**< XML data value, prefixes map to XML namespace prefixes */ LY_VALUE_JSON, /**< JSON data value, prefixes map to module names */ + LY_VALUE_CBOR, /**< CBOR data value, prefixes map to module names */ LY_VALUE_LYB, /**< LYB data binary value, prefix mapping is type-specific (but usually like JSON) */ LY_VALUE_STR_NS /**< any data format value, prefixes map to XML namespace prefixes */ } LY_VALUE_FORMAT; diff --git a/src/tree_data.c b/src/tree_data.c index a688c7233..1156eae67 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -35,6 +35,7 @@ #include "in_internal.h" #include "log.h" #include "ly_common.h" +#include "parser_cbor.h" #include "parser_data.h" #include "parser_internal.h" #include "path.h" @@ -78,6 +79,9 @@ lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) } else if ((len >= LY_LYB_SUFFIX_LEN + 1) && !strncmp(&path[len - LY_LYB_SUFFIX_LEN], LY_LYB_SUFFIX, LY_LYB_SUFFIX_LEN)) { format = LYD_LYB; + } else if ((len >= LY_CBOR_SUFFIX_LEN + 1) && + !strncmp(&path[len - LY_CBOR_SUFFIX_LEN], LY_CBOR_SUFFIX, LY_CBOR_SUFFIX_LEN)) { + format = LYD_CBOR; } /* else still unknown */ } @@ -88,6 +92,7 @@ lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) * @brief Parse YANG data into a data tree. * * @param[in] ctx libyang context. + * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance * @param[in] parent Parent to connect the parsed nodes to, if any. * @param[in,out] first_p Pointer to the first parsed node. * @param[in] in Input handle to read the input from. @@ -131,6 +136,10 @@ lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **f case LYD_LYB: r = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx); break; + case LYD_CBOR: + r = lyd_parse_cbor(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, + &subtree_sibling, &lydctx); + break; case LYD_UNKNOWN: LOGARG(ctx, format); r = LY_EINVAL; @@ -206,6 +215,22 @@ lyd_parse_data(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in * return lyd_parse(ctx, parent, tree, in, format, parse_options, validate_options, NULL); } +LIBYANG_API_DEF LY_ERR +lyd_parse_data_mem_len(const struct ly_ctx *ctx, const char *data, size_t data_len, LYD_FORMAT format, + uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) +{ + LY_ERR ret; + struct ly_in *in; + + LY_CHECK_RET(ly_in_new_memory(data, &in)); + in->length = data_len; // Set the length for the input + + ret = lyd_parse_data(ctx, NULL, in, format, parse_options, validate_options, tree); + + ly_in_free(in, 0); + return ret; +} + LIBYANG_API_DEF LY_ERR lyd_parse_data_mem(const struct ly_ctx *ctx, const char *data, LYD_FORMAT format, uint32_t parse_options, uint32_t validate_options, struct lyd_node **tree) @@ -460,6 +485,9 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in case LYD_LYB: rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); break; + case LYD_CBOR: + rc = lyd_parse_cbor(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); + break; case LYD_UNKNOWN: LOGARG(ctx, format); rc = LY_EINVAL; diff --git a/src/tree_data.h b/src/tree_data.h index 54d75d3c1..1e074b49e 100644 --- a/src/tree_data.h +++ b/src/tree_data.h @@ -513,9 +513,26 @@ typedef enum { LYD_UNKNOWN = 0, /**< unknown data format, invalid value */ LYD_XML, /**< XML instance data format */ LYD_JSON, /**< JSON instance data format */ - LYD_LYB /**< LYB instance data format */ + LYD_LYB, /**< LYB instance data format */ + LYD_CBOR /**< CBOR instance data format */ } LYD_FORMAT; +/** + * @brief List of possible value types stored in ::lyd_node_any. + */ +typedef enum { + LYD_ANYDATA_DATATREE, /**< Value is a pointer to ::lyd_node structure (first sibling). When provided as input + parameter, the pointer is directly connected into the anydata node without duplication, + caller is supposed to not manipulate with the data after a successful call (including + calling ::lyd_free_all() on the provided data) */ + LYD_ANYDATA_STRING, /**< Value is a generic string without any knowledge about its format (e.g. anyxml value in + JSON encoded as string). XML sensitive characters (such as & or \>) are automatically + escaped when the anydata is printed in XML format. */ + LYD_ANYDATA_XML, /**< Value is a string containing the serialized XML data. */ + LYD_ANYDATA_JSON, /**< Value is a string containing the data modeled by YANG and encoded as I-JSON. */ + LYD_ANYDATA_LYB /**< Value is a memory chunk with the serialized data tree in LYB format. */ +} LYD_ANYDATA_VALUETYPE; + /** @} */ /** diff --git a/src/tree_data_internal.h b/src/tree_data_internal.h index 919c4dd54..82dedb916 100644 --- a/src/tree_data_internal.h +++ b/src/tree_data_internal.h @@ -33,6 +33,8 @@ struct lysc_module; #define LY_JSON_SUFFIX_LEN 5 #define LY_LYB_SUFFIX ".lyb" #define LY_LYB_SUFFIX_LEN 4 +#define LY_CBOR_SUFFIX ".cbor" +#define LY_CBOR_SUFFIX_LEN 5 /** * @brief Internal item structure for remembering "used" instances of duplicate node instances. From 10f35823644613c7a5b767bdfdfd0d398beb4609 Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Wed, 23 Jul 2025 21:11:22 +0530 Subject: [PATCH 2/8] refactor: restructure CBOR parser to match libyang conventions --- CMakeLists.txt | 12 +- src/lcbor.c | 89 +++ src/lcbor.h | 71 +++ src/parser_cbor.c | 1255 ++++++++++++++++++++++------------------- src/parser_cbor.h | 279 --------- src/parser_internal.h | 54 ++ src/tree_data.c | 1 - 7 files changed, 887 insertions(+), 874 deletions(-) create mode 100644 src/lcbor.c create mode 100644 src/lcbor.h delete mode 100644 src/parser_cbor.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 590847d93..9b12102fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,10 +334,10 @@ if(ENABLE_CBOR_SUPPORT) add_definitions(-DENABLE_CBOR_SUPPORT) include_directories(${LIBCBOR_INCLUDE_DIRS}) # Add CBOR parser files to the library sources - list(APPEND libsrc src/parser_cbor.c) - list(APPEND headers src/parser_cbor.h) + list(APPEND libsrc src/parser_cbor.c src/lcbor.c) + list(APPEND headers src/lcbor.h) # Add CBOR files to format sources - list(APPEND format_sources src/parser_cbor.c src/parser_cbor.h) + list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c) else() message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") endif() @@ -351,10 +351,10 @@ if(ENABLE_CBOR_SUPPORT) include_directories(${LIBCBOR_INCLUDE_DIR}) set(LIBCBOR_LIBRARIES ${LIBCBOR_LIBRARY}) # Add CBOR parser files to the library sources - list(APPEND libsrc src/parser_cbor.c) - list(APPEND headers src/parser_cbor.h) + list(APPEND libsrc src/parser_cbor.c src/lcbor.c) + list(APPEND headers src/lcbor.h) # Add CBOR files to format sources - list(APPEND format_sources src/parser_cbor.c src/parser_cbor.h) + list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c) else() message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") endif() diff --git a/src/lcbor.c b/src/lcbor.c new file mode 100644 index 000000000..e3962fa46 --- /dev/null +++ b/src/lcbor.c @@ -0,0 +1,89 @@ +/** + * @file lcbor.h + * @author MeherRushi + * @brief CBOR data parser for libyang (abstraction over libcbor) + * + * Copyright (c) 2020 - 2023 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 + */ + +#ifdef ENABLE_CBOR_SUPPORT + +#include +#include +#include + +#include "in_internal.h" +#include "lcbor.h" +#include "log.h" +#include "ly_common.h" + +/** + * @brief Free CBOR context. + * + * @param[in] cbor_ctx CBOR context to free. + */ +void lycbor_ctx_free(struct lycbor_ctx *cbor_ctx) +{ + if (cbor_ctx) + { + free(cbor_ctx); + } +} + +/** + * @brief Detect CBOR format variant from input data. + * + * @param[in] in Input structure to analyze. + * @param[out] format Detected format. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) +{ + /* Simple heuristic: try to parse as CBOR and examine structure */ + /* For now, default to named format */ + (void)in; + *format = LYD_CBOR_NAMED; + return LY_SUCCESS; +} + +/** + * @brief Create new CBOR context for parsing. + * + * @param[in] ctx libyang context. + * @param[in] in Input handler. + * @param[out] cbor_ctx_p Pointer to store the created CBOR context. + * @return LY_ERR value. + */ +LY_ERR +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p) +{ + /* TODO : Need to restructure error handling here */ + LY_ERR ret = LY_SUCCESS; + struct lycbor_ctx *cbor_ctx; + enum lyd_cbor_format format; + + assert(ctx && in && cbor_ctx_p); + + /* TODO : error handling after the detect_format function call */ + ret = lydcbor_detect_format(in, &format); + + /* Allocate and initialize CBOR context */ + cbor_ctx = calloc(1, sizeof *cbor_ctx); + LY_CHECK_ERR_RET(!cbor_ctx, LOGMEM(ctx), LY_EMEM); + + cbor_ctx->ctx = ctx; + cbor_ctx->in = in; + cbor_ctx->format = format; + + *cbor_ctx_p = cbor_ctx; + return ret; +} + +#endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file diff --git a/src/lcbor.h b/src/lcbor.h new file mode 100644 index 000000000..22d2fb486 --- /dev/null +++ b/src/lcbor.h @@ -0,0 +1,71 @@ +/** + * @file lcbor.h + * @author MeherRushi + * @brief CBOR data parser routines for libyang (abstraction over libcbor) + * + * Copyright (c) 2020 - 2023 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_LCBOR_H_ +#define LY_LCBOR_H_ + +#ifdef ENABLE_CBOR_SUPPORT + +#include +#include +/* using libcbor as the low-level parser */ +#include + + +#include "log.h" +#include "set.h" + +struct ly_ctx; +struct ly_in; + +/** + * @brief CBOR format variants for different encoding schemes + */ +enum lyd_cbor_format +{ + LYD_CBOR_NAMED, /**< CBOR with named identifiers (JSON-like) */ + LYD_CBOR_SID /**< CBOR with Schema Item identifiers (future implementation) */ +}; + +struct lycbor_ctx { + const struct ly_ctx *ctx; /**< libyang context */ + struct ly_in *in; /**< input structure */ + cbor_item_t *cbor_data; /**< parsed CBOR data */ + enum lyd_cbor_format format; /**< CBOR format variant */ + uint32_t parse_opts; /**< parser options */ + uint32_t val_opts; /**< validation options */ +}; + +/** + * @brief Create new CBOR context for parsing. + * + * @param[in] ctx libyang context. + * @param[in] in Input handler. + * @param[out] cbor_ctx_p Pointer to store the created CBOR context. + * @return LY_ERR value. + */ +LY_ERR +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p); + +/** + * @brief Free CBOR context. + * + * @param[in] cbor_ctx CBOR context to free. + */ +void +lycbor_ctx_free(struct lycbor_ctx *cbor_ctx); + +#endif /* ENABLE_CBOR_SUPPORT */ + +#endif /* LY_LCBOR_H_ */ \ No newline at end of file diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 38104f31e..bcc12d2aa 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -1,6 +1,6 @@ /** * @file parser_cbor.c - * @author + * @author Meher Rushi * @brief CBOR data parser for libyang * * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. @@ -16,18 +16,16 @@ #ifdef ENABLE_CBOR_SUPPORT -#include "parser_cbor.h" - #include #include #include #include -#include #include "compat.h" #include "context.h" #include "dict.h" #include "in_internal.h" +#include "lcbor.h" #include "log.h" #include "ly_common.h" #include "parser_data.h" @@ -40,51 +38,65 @@ #include "tree_schema.h" #include "validation.h" -#include "cbor.h" - #include #include void print_json(cbor_item_t *item); -void print_json_string(const cbor_item_t *item) { +void print_json_string(const cbor_item_t *item) +{ size_t length = cbor_string_length(item); char *str = (char *)cbor_string_handle(item); printf("\"%.*s\"", (int)length, str); } -void print_json_map(const cbor_item_t *item) { +void print_json_map(const cbor_item_t *item) +{ printf("{"); size_t size = cbor_map_size(item); struct cbor_pair *pairs = cbor_map_handle(item); - for (size_t i = 0; i < size; ++i) { + for (size_t i = 0; i < size; ++i) + { print_json(pairs[i].key); printf(": "); print_json(pairs[i].value); - if (i < size - 1) printf(", "); + if (i < size - 1) + printf(", "); } printf("}"); } -void print_json_bool(const cbor_item_t *item) { +void print_json_bool(const cbor_item_t *item) +{ printf(cbor_is_bool(item) && cbor_ctrl_value(item) ? "true" : "false"); } -void print_json(cbor_item_t *item) { - if (cbor_isa_map(item)) { +void print_json(cbor_item_t *item) +{ + if (cbor_isa_map(item)) + { print_json_map(item); - } else if (cbor_isa_string(item)) { + } + else if (cbor_isa_string(item)) + { print_json_string(item); - } else if (cbor_is_bool(item)) { + } + else if (cbor_is_bool(item)) + { print_json_bool(item); - } else { + } + else + { printf("null"); // fallback for unsupported types } } +static LY_ERR lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); + /** * @brief Free the CBOR parser context * @@ -95,90 +107,14 @@ lyd_cbor_ctx_free(struct lyd_ctx *lydctx) { struct lyd_cbor_ctx *ctx = (struct lyd_cbor_ctx *)lydctx; - if(lydctx){ + if (lydctx) + { lyd_ctx_free(lydctx); lycbor_ctx_free(ctx->cborctx); free(ctx); } } -/** - * @brief Create new CBOR context for parsing. - * - * @param[in] ctx libyang context. - * @param[in] in Input handler. - * @param[out] cbor_ctx_p Pointer to store the created CBOR context. - * @return LY_ERR value. - */ -LY_ERR -lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p) -{ - LY_ERR ret = LY_SUCCESS; - struct lycbor_ctx *cbor_ctx; - - assert(ctx && in && cbor_ctx_p); - - /* Allocate and initialize CBOR context */ - cbor_ctx = calloc(1, sizeof *cbor_ctx); - LY_CHECK_ERR_RET(!cbor_ctx, LOGMEM(ctx), LY_EMEM); - - cbor_ctx->ctx = ctx; - cbor_ctx->in = in; - - *cbor_ctx_p = cbor_ctx; - return ret; -} - -/** - * @brief Free CBOR context. - * - * @param[in] cbor_ctx CBOR context to free. - */ -void -lycbor_ctx_free(struct lycbor_ctx *cbor_ctx) -{ - if (cbor_ctx) { - free(cbor_ctx); - } -} - -/** - * @brief Create new CBOR parser context - */ -LY_ERR -lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, - uint32_t parse_opts, uint32_t val_opts, enum lyd_cbor_format format, - struct lyd_cbor_ctx **lydctx_p) -{ - LY_ERR ret = LY_SUCCESS; - struct lyd_cbor_ctx *lydctx = NULL; - - assert(lydctx_p); - - /* Initialize context with calloc to ensure all fields are zero */ - lydctx = calloc(1, sizeof *lydctx); - LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM); - lydctx->parse_opts = parse_opts; - lydctx->val_opts = val_opts; - lydctx->free = lyd_cbor_ctx_free; - lydctx->format = format; - - lydctx->cborctx = NULL; /* Will be set below */ - - /* Create low-level CBOR context */ - LY_CHECK_GOTO(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), cleanup); - - *lydctx_p = lydctx; - return ret; - -cleanup: - if (lydctx) { - lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); - } - return ret; -} - - /** * @brief Convert a CBOR item to a string representation. * @@ -205,7 +141,8 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) { uint64_t val = cbor_get_int(item); int len = snprintf(NULL, 0, "%" PRIu64, val); - if (len < 0) { + if (len < 0) + { return LY_ESYS; } *str_val = malloc(len + 1); @@ -218,7 +155,8 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) { int64_t val = -1 - (int64_t)cbor_get_int(item); int len = snprintf(NULL, 0, "%" PRId64, val); - if (len < 0) { + if (len < 0) + { return LY_ESYS; } *str_val = malloc(len + 1); @@ -270,7 +208,8 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) /* Float value */ double val = cbor_float_get_float(item); int len = snprintf(NULL, 0, "%g", val); - if (len < 0) { + if (len < 0) + { return LY_ESYS; } *str_val = malloc(len + 1); @@ -302,7 +241,7 @@ lydcbor_get_key_string(struct lyd_cbor_ctx *lydctx, const cbor_item_t *key_item, assert(lydctx && key_item && key_str && key_len); - switch (lydctx->format) + switch (lydctx->cborctx->format) { case LYD_CBOR_NAMED: /* Keys must be strings for named format */ @@ -327,7 +266,16 @@ lydcbor_get_key_string(struct lyd_cbor_ctx *lydctx, const cbor_item_t *key_item, return ret; } -LY_ERR +/** + * @brief Parse a single CBOR value according to schema node. + * + * @param[in] lydctx CBOR parser context. + * @param[in] snode Schema node for the value. + * @param[in] cbor_item CBOR item to parse. + * @param[out] node Created data node. + * @return LY_ERR value. + */ +static LY_ERR lydcbor_parse_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, const void *cbor_item, struct lyd_node **node) { @@ -364,228 +312,17 @@ lydcbor_parse_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, return ret; } - -/** - * @brief Get module prefix from a qualified name. - * - * @param[in] qname Qualified name (prefix:name or just name). - * @param[in] qname_len Length of the qualified name. - * @param[out] prefix Extracted prefix (points into qname, not allocated). - * @param[out] prefix_len Length of the prefix. - * @param[out] name Local name (points into qname, not allocated). - * @param[out] name_len Length of the local name. - * @return LY_SUCCESS on success. - */ -static LY_ERR -lydcbor_parse_qname(const char *qname, size_t qname_len, const char **prefix, size_t *prefix_len, - const char **name, size_t *name_len) -{ - const char *colon; - - assert(qname && name && name_len); - - *name = qname; - *name_len = qname_len; - - if (prefix) { - *prefix = NULL; - } - if (prefix_len) { - *prefix_len = 0; - } - - /* Look for module prefix separator */ - colon = ly_strnchr(qname, ':', qname_len); - if (colon) { - /* We have a module prefix */ - if (prefix) { - *prefix = qname; - *prefix_len = colon - qname; - } - - /* Local name starts after the colon */ - *name = colon + 1; - *name_len = qname_len - (colon - qname) - 1; - - /* Validate we have both prefix and name */ - if ((colon == qname) || (*name_len == 0)) { - return LY_EVALID; - } - } - - return LY_SUCCESS; -} - -/** - * @brief Get schema node from CBOR node name, following lydjson_get_snode logic. - * - * @param[in] lydctx CBOR parser context. - * @param[in] name Node name. - * @param[in] name_len Length of node name. - * @param[in] parent Data parent node. - * @param[out] snode Schema node found. - * @param[out] ext Extension instance if found. - * @return LY_ERR value. - */ -static LY_ERR -lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, - struct lyd_node *parent, const struct lysc_node **snode, - const struct lysc_ext_instance **ext) -{ - LY_ERR ret = LY_SUCCESS, r; - const char *prefix = NULL, *local_name = NULL; - size_t prefix_len = 0, local_name_len = 0; - const struct lys_module *mod = NULL; - const struct lysc_node *sparent = NULL; - uint32_t getnext_opts; - - assert(lydctx && name && snode); - *snode = NULL; - if (ext) { - *ext = NULL; - } - - /* Parse qualified name */ - LY_CHECK_RET(lydcbor_parse_qname(name, name_len, &prefix, &prefix_len, &local_name, &local_name_len)); - - /* Get parent schema node */ - if (parent) { - sparent = parent->schema; - if (!sparent) { - /* Opaque parent */ - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Cannot parse \"%.*s\" node with opaque parent.", - (int)local_name_len, local_name); - ret = LY_EVALID; - goto cleanup; - } - } else { - sparent = NULL; - } - - /* Resolve module if prefix is present */ - if (prefix) { - mod = ly_ctx_get_module_implemented2(lydctx->cborctx->ctx, prefix, prefix_len); - if (!mod) { - if (lydctx->parse_opts & LYD_PARSE_STRICT) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); - ret = LY_EVALID; - goto cleanup; - } - if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); - ret = LY_EVALID; - goto cleanup; - } - } - } else if (!sparent) { - /* Top-level node without prefix - need to find module */ - /* Try to find the node in all implemented modules */ - const struct lys_module *iter_mod; - uint32_t idx = 0; - ly_bool found = 0; - - while ((iter_mod = ly_ctx_get_module_iter(lydctx->cborctx->ctx, &idx))) { - if (!iter_mod->implemented) { - continue; - } - - /* Check if node exists in this module */ - if (lys_find_child(NULL, iter_mod, local_name, local_name_len, 0, 0)) { - if (found) { - /* Ambiguous name */ - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Ambiguous node name \"%.*s\", use module prefix.", - (int)local_name_len, local_name); - ret = LY_EVALID; - goto cleanup; - } - mod = iter_mod; - found = 1; - } - } - - if (!found && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unknown node \"%.*s\".", (int)local_name_len, local_name); - ret = LY_EVALID; - goto cleanup; - } - } - - /* Set getnext options */ - getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; - if (parent && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) { - if (lydctx->int_opts & LYD_INTOPT_RPC) { - getnext_opts = 0; - } else if (lydctx->int_opts & LYD_INTOPT_REPLY) { - getnext_opts = LYS_GETNEXT_OUTPUT; - } - } - - /* Find schema node */ - if (sparent) { - /* Search in parent's children */ - *snode = lys_find_child(sparent, sparent->module, local_name, local_name_len, 0, getnext_opts); - - /* Try to find extension data if regular node not found */ - if (!*snode && ext) { - r = ly_nested_ext_schema(parent, sparent, prefix, prefix_len, LY_VALUE_JSON, NULL, - local_name, local_name_len, snode, ext); - if (r != LY_ENOT) { - if (r) { - ret = r; - goto cleanup; - } - } - } - } else { - /* Top-level node */ - if (mod) { - /* Search in specific module */ - *snode = lys_find_child(NULL, mod, local_name, local_name_len, 0, getnext_opts); - } - /* Extension data for top-level not typically handled */ - } - - /* Handle missing schema node */ - if (!*snode) { - if (lydctx->parse_opts & LYD_PARSE_STRICT) { - if (prefix) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", - (int)local_name_len, local_name, (int)prefix_len, prefix); - } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", - (int)local_name_len, local_name); - } - ret = LY_EVALID; - goto cleanup; - } else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) { - /* Log error but continue if not in strict mode and opaque allowed */ - if (prefix) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", - (int)local_name_len, local_name, (int)prefix_len, prefix); - } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", - (int)local_name_len, local_name); - } - ret = LY_EVALID; - goto cleanup; - } - /* If opaque parsing allowed, *snode remains NULL and caller handles it */ - } - -cleanup: - return ret; -} - static LY_ERR lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - struct lyd_node **node, const cbor_item_t *cbor_value) + struct lyd_node **node, const cbor_item_t *cbor_value) { LY_ERR ret = LY_SUCCESS; - + assert(lydctx && snode && node && cbor_value); *node = NULL; - - switch (snode->nodetype) { + + switch (snode->nodetype) + { case LYS_CONTAINER: ret = lyd_create_inner(snode, node); break; @@ -593,12 +330,14 @@ lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sn ret = lyd_create_inner(snode, node); break; case LYS_LEAF: - case LYS_LEAFLIST: { + case LYS_LEAFLIST: + { char *str_val = NULL; size_t str_len = 0; - + ret = lydcbor_item_to_string(cbor_value, &str_val, &str_len); - if (ret == LY_SUCCESS) { + if (ret == LY_SUCCESS) + { ret = lyd_create_term(snode, str_val, str_len, 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, node); } free(str_val); @@ -613,227 +352,89 @@ lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sn ret = LY_EVALID; break; } - + return ret; } static LY_ERR -lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) +lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) { - LY_ERR ret = LY_SUCCESS; + LY_ERR ret; struct lyd_node *node = NULL; - size_t array_size; - cbor_item_t **array_handle; - assert(lydctx && snode && array_item && parsed); - - if (!cbor_isa_array(array_item)) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for leaf-list"); - return LY_EVALID; - } - - if (snode->nodetype != LYS_LEAFLIST) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be leaf-list"); - return LY_EVALID; - } - - array_size = cbor_array_size(array_item); - array_handle = cbor_array_handle(array_item); - - if (!array_handle && array_size > 0) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); - return LY_EVALID; - } - - for (size_t i = 0; i < array_size; ++i) { - const cbor_item_t *item = array_handle[i]; - - if (!item) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); - ret = LY_EVALID; - goto cleanup; - } - - LY_CHECK_GOTO(ret = lydcbor_parse_node_value(lydctx, snode, &node, item), cleanup); - - if (!node) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create node for array element %zu", i); - ret = LY_EVALID; - goto cleanup; - } - - /* Insert the node */ - ret = lyd_insert_sibling(*first_p, node, first_p); - LY_CHECK_GOTO(ret, cleanup); - - /* Add to parsed set */ - LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); - node = NULL; /* Reset pointer after successful insertion */ - } - -cleanup: - if (ret && node) { - lyd_free_tree(node); - } - return ret; -} - -static LY_ERR -lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj) -{ - LY_ERR ret = LY_SUCCESS; - - printf("Entering lydcbor_parse_subtree\n"); - printf("CBOR object:\n"); - print_json(cbor_obj); - printf("\n"); - - const struct lysc_node *snode = NULL; - char *key_str = NULL; - size_t key_len = 0; -\ - assert(lydctx && first_p && parsed && cbor_obj); - - /* assuming that the top level structure is always a map - to be modified to include anything else that it can support */ - - if (!cbor_isa_map(cbor_obj)) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR map"); - return LY_EVALID; - } - - size_t map_size = cbor_map_size(cbor_obj); - struct cbor_pair *pairs = cbor_map_handle(cbor_obj); - - if (!pairs && map_size > 0) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR map structure"); - return LY_EVALID; - } - - for (size_t i = 0; i < map_size; ++i) { - const cbor_item_t *key_item = pairs[i].key; - const cbor_item_t *value_item = pairs[i].value; - - if (!key_item || !value_item) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null key or value at map index %zu", i); - ret = LY_EVALID; - goto cleanup; - } - - /* Get key string */ - LY_CHECK_GOTO(ret = lydcbor_get_key_string(lydctx, key_item, &key_str, &key_len), cleanup); - - /* Find schema node */ - LY_CHECK_GOTO(ret = lydcbor_get_snode(lydctx, key_str, key_len, - parent, &snode, NULL), cleanup); - - /* Handle different node types */ - if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) { - if (snode->nodetype == LYS_LEAFLIST && cbor_isa_array(value_item)) { - ret = lydcbor_parse_leaflist_array(lydctx, snode, value_item, first_p, parsed); - } else { - ret = lydcbor_parse_terminal(lydctx, snode, value_item, first_p, parsed); - } - } else if (snode->nodetype == LYS_CONTAINER) { - ret = lydcbor_parse_container(lydctx, snode, value_item, first_p, parsed); - } else if (snode->nodetype == LYS_LIST) { - ret = lydcbor_parse_list(lydctx, snode, value_item, first_p, parsed); - } else if (snode->nodetype & (LYS_ANYDATA | LYS_ANYXML)) { - ret = lydcbor_parse_any(lydctx, snode, value_item, first_p, parsed); - } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type %d for \"%s\"", - snode->nodetype, snode->name); - ret = LY_EVALID; - } - - LY_CHECK_GOTO(ret, cleanup); - - free(key_str); - key_str = NULL; - } - -cleanup: - free(key_str); - return ret; -} - -// Add these new functions: - -static LY_ERR -lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) -{ - LY_ERR ret; - struct lyd_node *node = NULL; - ret = lydcbor_parse_node_value(lydctx, snode, &node, cbor_value); LY_CHECK_RET(ret); - - if (!node) { + + if (!node) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create terminal node for \"%s\"", snode->name); return LY_EVALID; } - + /* Insert into tree */ ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) { + if (ret) + { lyd_free_tree(node); return ret; } - + /* Add to parsed set */ return ly_set_add(parsed, node, 1, NULL); } static LY_ERR lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) { LY_ERR ret; struct lyd_node *node = NULL; - + ret = lyd_create_inner(snode, &node); LY_CHECK_RET(ret); - - if (!node) { + + if (!node) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create container node for \"%s\"", snode->name); return LY_EVALID; } - + /* Insert into tree first */ ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) { + if (ret) + { lyd_free_tree(node); return ret; } - + /* Add to parsed set */ ret = ly_set_add(parsed, node, 1, NULL); LY_CHECK_RET(ret); - + /* Parse container children */ - if (cbor_isa_map(cbor_value) && cbor_map_size(cbor_value) > 0) { + if (cbor_isa_map(cbor_value) && cbor_map_size(cbor_value) > 0) + { struct lyd_node *child_first = NULL; ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, cbor_value); - if (ret) { + if (ret) + { return ret; } - + /* Link children to container */ - if (child_first) { + if (child_first) + { lyd_insert_child(node, child_first); } } - + return LY_SUCCESS; } -// Replace lydcbor_parse_list_array with this improved version: static LY_ERR lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) + const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) { LY_ERR ret = LY_SUCCESS; struct lyd_node *node = NULL; @@ -841,71 +442,80 @@ lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sn cbor_item_t **array_handle; assert(lydctx && snode && array_item && parsed); - - if (!cbor_isa_array(array_item)) { + + if (!cbor_isa_array(array_item)) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for list"); return LY_EVALID; } - - if (snode->nodetype != LYS_LIST) { + + if (snode->nodetype != LYS_LIST) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be list"); return LY_EVALID; } array_size = cbor_array_size(array_item); array_handle = cbor_array_handle(array_item); - - if (!array_handle && array_size > 0) { + + if (!array_handle && array_size > 0) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); return LY_EVALID; } - for (size_t i = 0; i < array_size; ++i) { + for (size_t i = 0; i < array_size; ++i) + { const cbor_item_t *item = array_handle[i]; - - if (!item) { + + if (!item) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); ret = LY_EVALID; goto cleanup; } - - if (!cbor_isa_map(item)) { + + if (!cbor_isa_map(item)) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "List entry must be a CBOR map"); ret = LY_EVALID; goto cleanup; } - + ret = lyd_create_inner(snode, &node); LY_CHECK_GOTO(ret, cleanup); - - if (!node) { + + if (!node) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create list node"); ret = LY_EVALID; goto cleanup; } - + /* Insert the list node */ ret = lyd_insert_sibling(*first_p, node, first_p); LY_CHECK_GOTO(ret, cleanup); - + /* Add to parsed set */ LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); - + /* Parse list entry content */ struct lyd_node *child_first = NULL; ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, item); LY_CHECK_GOTO(ret, cleanup); - + /* Link children to list entry */ - if (child_first) { + if (child_first) + { lyd_insert_child(node, child_first); } - + node = NULL; /* Reset pointer after successful processing */ } cleanup: - if (ret && node) { + if (ret && node) + { lyd_free_tree(node); } return ret; @@ -913,87 +523,96 @@ lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sn static LY_ERR lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) { LY_ERR ret = LY_SUCCESS; - - if (cbor_isa_array(cbor_value)) { + + if (cbor_isa_array(cbor_value)) + { /* Array of list entries */ ret = lydcbor_parse_list_array(lydctx, snode, cbor_value, first_p, parsed); - } else if (cbor_isa_map(cbor_value)) { + } + else if (cbor_isa_map(cbor_value)) + { /* Single list entry */ struct lyd_node *node = NULL; - + ret = lyd_create_inner(snode, &node); LY_CHECK_RET(ret); - + /* Insert into tree */ ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) { + if (ret) + { lyd_free_tree(node); return ret; } - + /* Add to parsed set */ ret = ly_set_add(parsed, node, 1, NULL); LY_CHECK_RET(ret); - + /* Parse list entry content */ struct lyd_node *child_first = NULL; ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, cbor_value); - if (ret) { + if (ret) + { return ret; } - + /* Link children to list entry */ - if (child_first) { + if (child_first) + { lyd_insert_child(node, child_first); } - } else { + } + else + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "List \"%s\" value must be a CBOR map or array", snode->name); ret = LY_EVALID; } - + return ret; } static LY_ERR lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) { LY_ERR ret; struct lyd_node *node = NULL; - + ret = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, &node); LY_CHECK_RET(ret); - - if (!node) { + + if (!node) + { LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create any node for \"%s\"", snode->name); return LY_EVALID; } - + /* Insert into tree */ ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) { + if (ret) + { lyd_free_tree(node); return ret; } - + /* Add to parsed set */ return ly_set_add(parsed, node, 1, NULL); } -LY_ERR -lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) -{ - /* Simple heuristic: try to parse as CBOR and examine structure */ - /* For now, default to named format */ - (void)in; - *format = LYD_CBOR_NAMED; - return LY_SUCCESS; -} -LY_ERR +/** + * @brief Parse CBOR metadata/attributes. + * + * @param[in] lydctx CBOR parser context. + * @param[in] cbor_item CBOR item containing metadata. + * @param[in,out] node Data node to attach metadata to. + * @return LY_ERR value. + */ +static LY_ERR lydcbor_parse_metadata(struct lyd_cbor_ctx *lydctx, const void *cbor_item, struct lyd_node *node) { /* Future implementation for CBOR metadata parsing */ @@ -1003,67 +622,527 @@ lydcbor_parse_metadata(struct lyd_cbor_ctx *lydctx, const void *cbor_item, struc return LY_SUCCESS; } -LY_ERR -lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, - struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) -{ - LY_ERR ret = LY_SUCCESS; - struct lyd_cbor_ctx *lydctx = NULL; - cbor_item_t *cbor_data = NULL; - struct cbor_load_result result = {0}; - enum lyd_cbor_format format; - /* Detect CBOR format - Named or SID */ - LY_CHECK_GOTO(ret = lydcbor_detect_format(in, &format), cleanup); +/** + * @brief Get module prefix from a qualified name. + * + * @param[in] qname Qualified name (prefix:name or just name). + * @param[in] qname_len Length of the qualified name. + * @param[out] prefix Extracted prefix (points into qname, not allocated). + * @param[out] prefix_len Length of the prefix. + * @param[out] name Local name (points into qname, not allocated). + * @param[out] name_len Length of the local name. + * @return LY_SUCCESS on success. + */ +static LY_ERR +lydcbor_parse_qname(const char *qname, size_t qname_len, const char **prefix, size_t *prefix_len, + const char **name, size_t *name_len) +{ + const char *colon; - /* Initialize context */ - LY_CHECK_GOTO(ret = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, format, &lydctx), cleanup); - - lydctx->int_opts = int_opts; - lydctx->ext = ext; + assert(qname && name && name_len); - /* - * Loads CBOR data from the current input buffer. - * - * Parameters: - * in->current - Pointer to the current position in the input buffer. - * in->length - Length of the data to be loaded. - * &result - Pointer to a variable where the result status will be stored. - * - * Returns: - * cbor_data - Pointer to the loaded CBOR data structure, or NULL on failure. - */ - /* need to convert in->current from const char* to cbor_data type */ - cbor_data = cbor_load(in->current, in->length, &result); - lydctx->cborctx->cbor_data = cbor_data; + *name = qname; + *name_len = qname_len; - if (!cbor_data) { - LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: no data returned from cbor_load()."); - ret = LY_EVALID; - goto cleanup; + if (prefix) + { + *prefix = NULL; } - if (result.error.code != CBOR_ERR_NONE) { - LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: parsing error (code %d).", result.error.code); - ret = LY_EVALID; - goto cleanup; + if (prefix_len) + { + *prefix_len = 0; } - /* Probably need to check if the obtained data is a operational node and - then write functions to parse them accordingly. If not then continue below */ + /* Look for module prefix separator */ + colon = ly_strnchr(qname, ':', qname_len); + if (colon) + { + /* We have a module prefix */ + if (prefix) + { + *prefix = qname; + *prefix_len = colon - qname; + } - /* Parse the CBOR structure */ - ret = lydcbor_parse_subtree(lydctx, parent, first_p, parsed, cbor_data); + /* Local name starts after the colon */ + *name = colon + 1; + *name_len = qname_len - (colon - qname) - 1; -cleanup: - if (cbor_data) - { - cbor_decref(&cbor_data); + /* Validate we have both prefix and name */ + if ((colon == qname) || (*name_len == 0)) + { + return LY_EVALID; + } } - if (ret) - { - if (lydctx) { + return LY_SUCCESS; +} + +/** + * @brief Get schema node from CBOR node name, following lydjson_get_snode logic. + * + * @param[in] lydctx CBOR parser context. + * @param[in] name Node name. + * @param[in] name_len Length of node name. + * @param[in] parent Data parent node. + * @param[out] snode Schema node found. + * @param[out] ext Extension instance if found. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, + struct lyd_node *parent, const struct lysc_node **snode, + const struct lysc_ext_instance **ext) +{ + LY_ERR ret = LY_SUCCESS, r; + const char *prefix = NULL, *local_name = NULL; + size_t prefix_len = 0, local_name_len = 0; + const struct lys_module *mod = NULL; + const struct lysc_node *sparent = NULL; + uint32_t getnext_opts; + + assert(lydctx && name && snode); + *snode = NULL; + if (ext) + { + *ext = NULL; + } + + /* Parse qualified name */ + LY_CHECK_RET(lydcbor_parse_qname(name, name_len, &prefix, &prefix_len, &local_name, &local_name_len)); + + /* Get parent schema node */ + if (parent) + { + sparent = parent->schema; + if (!sparent) + { + /* Opaque parent */ + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Cannot parse \"%.*s\" node with opaque parent.", + (int)local_name_len, local_name); + ret = LY_EVALID; + goto cleanup; + } + } + else + { + sparent = NULL; + } + + /* Resolve module if prefix is present */ + if (prefix) + { + mod = ly_ctx_get_module_implemented2(lydctx->cborctx->ctx, prefix, prefix_len); + if (!mod) + { + if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); + ret = LY_EVALID; + goto cleanup; + } + if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); + ret = LY_EVALID; + goto cleanup; + } + } + } + else if (!sparent) + { + /* Top-level node without prefix - need to find module */ + /* Try to find the node in all implemented modules */ + const struct lys_module *iter_mod; + uint32_t idx = 0; + ly_bool found = 0; + + while ((iter_mod = ly_ctx_get_module_iter(lydctx->cborctx->ctx, &idx))) + { + if (!iter_mod->implemented) + { + continue; + } + + /* Check if node exists in this module */ + if (lys_find_child(NULL, iter_mod, local_name, local_name_len, 0, 0)) + { + if (found) + { + /* Ambiguous name */ + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Ambiguous node name \"%.*s\", use module prefix.", + (int)local_name_len, local_name); + ret = LY_EVALID; + goto cleanup; + } + mod = iter_mod; + found = 1; + } + } + + if (!found && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unknown node \"%.*s\".", (int)local_name_len, local_name); + ret = LY_EVALID; + goto cleanup; + } + } + + /* Set getnext options */ + getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; + if (parent && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) + { + if (lydctx->int_opts & LYD_INTOPT_RPC) + { + getnext_opts = 0; + } + else if (lydctx->int_opts & LYD_INTOPT_REPLY) + { + getnext_opts = LYS_GETNEXT_OUTPUT; + } + } + + /* Find schema node */ + if (sparent) + { + /* Search in parent's children */ + *snode = lys_find_child(sparent, sparent->module, local_name, local_name_len, 0, getnext_opts); + + /* Try to find extension data if regular node not found */ + if (!*snode && ext) + { + r = ly_nested_ext_schema(parent, sparent, prefix, prefix_len, LY_VALUE_JSON, NULL, + local_name, local_name_len, snode, ext); + if (r != LY_ENOT) + { + if (r) + { + ret = r; + goto cleanup; + } + } + } + } + else + { + /* Top-level node */ + if (mod) + { + /* Search in specific module */ + *snode = lys_find_child(NULL, mod, local_name, local_name_len, 0, getnext_opts); + } + /* Extension data for top-level not typically handled */ + } + + /* Handle missing schema node */ + if (!*snode) + { + if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + if (prefix) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", + (int)local_name_len, local_name, (int)prefix_len, prefix); + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", + (int)local_name_len, local_name); + } + ret = LY_EVALID; + goto cleanup; + } + else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) + { + /* Log error but continue if not in strict mode and opaque allowed */ + if (prefix) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", + (int)local_name_len, local_name, (int)prefix_len, prefix); + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", + (int)local_name_len, local_name); + } + ret = LY_EVALID; + goto cleanup; + } + /* If opaque parsing allowed, *snode remains NULL and caller handles it */ + } + +cleanup: + return ret; +} + +static LY_ERR +lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, + const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_node *node = NULL; + size_t array_size; + cbor_item_t **array_handle; + + assert(lydctx && snode && array_item && parsed); + + if (!cbor_isa_array(array_item)) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for leaf-list"); + return LY_EVALID; + } + + if (snode->nodetype != LYS_LEAFLIST) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be leaf-list"); + return LY_EVALID; + } + + array_size = cbor_array_size(array_item); + array_handle = cbor_array_handle(array_item); + + if (!array_handle && array_size > 0) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); + return LY_EVALID; + } + + for (size_t i = 0; i < array_size; ++i) + { + const cbor_item_t *item = array_handle[i]; + + if (!item) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + LY_CHECK_GOTO(ret = lydcbor_parse_node_value(lydctx, snode, &node, item), cleanup); + + if (!node) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create node for array element %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + /* Insert the node */ + ret = lyd_insert_sibling(*first_p, node, first_p); + LY_CHECK_GOTO(ret, cleanup); + + /* Add to parsed set */ + LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); + node = NULL; /* Reset pointer after successful insertion */ + } + +cleanup: + if (ret && node) + { + lyd_free_tree(node); + } + return ret; +} + +static LY_ERR +lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj) +{ + LY_ERR ret = LY_SUCCESS; + + printf("Entering lydcbor_parse_subtree\n"); + printf("CBOR object:\n"); + print_json(cbor_obj); + printf("\n"); + + const struct lysc_node *snode = NULL; + char *key_str = NULL; + size_t key_len = 0; + + assert(lydctx && first_p && parsed && cbor_obj); + + /* assuming that the top level structure is always a map + to be modified to include anything else that it can support */ + + if (!cbor_isa_map(cbor_obj)) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR map"); + return LY_EVALID; + } + + size_t map_size = cbor_map_size(cbor_obj); + struct cbor_pair *pairs = cbor_map_handle(cbor_obj); + + if (!pairs && map_size > 0) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR map structure"); + return LY_EVALID; + } + + for (size_t i = 0; i < map_size; ++i) + { + const cbor_item_t *key_item = pairs[i].key; + const cbor_item_t *value_item = pairs[i].value; + + if (!key_item || !value_item) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null key or value at map index %zu", i); + ret = LY_EVALID; + goto cleanup; + } + + /* Get key string */ + LY_CHECK_GOTO(ret = lydcbor_get_key_string(lydctx, key_item, &key_str, &key_len), cleanup); + + /* Find schema node */ + LY_CHECK_GOTO(ret = lydcbor_get_snode(lydctx, key_str, key_len, + parent, &snode, NULL), + cleanup); + + /* Handle different node types */ + if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) + { + if (snode->nodetype == LYS_LEAFLIST && cbor_isa_array(value_item)) + { + ret = lydcbor_parse_leaflist_array(lydctx, snode, value_item, first_p, parsed); + } + else + { + ret = lydcbor_parse_terminal(lydctx, snode, value_item, first_p, parsed); + } + } + else if (snode->nodetype == LYS_CONTAINER) + { + ret = lydcbor_parse_container(lydctx, snode, value_item, first_p, parsed); + } + else if (snode->nodetype == LYS_LIST) + { + ret = lydcbor_parse_list(lydctx, snode, value_item, first_p, parsed); + } + else if (snode->nodetype & (LYS_ANYDATA | LYS_ANYXML)) + { + ret = lydcbor_parse_any(lydctx, snode, value_item, first_p, parsed); + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type %d for \"%s\"", + snode->nodetype, snode->name); + ret = LY_EVALID; + } + + LY_CHECK_GOTO(ret, cleanup); + + free(key_str); + key_str = NULL; + } + +cleanup: + free(key_str); + return ret; +} + +/** + * @brief Create a new CBOR parser context. + * + * @param[in] ctx libyang context. + * @param[in] ext Extension instance providing context for the top level element, NULL if none. + * @param[in] parse_opts Parse options, see @ref dataparseroptions. + * @param[in] val_opts Validation options, see @ref datavalidationoptions. + * @param[in] format CBOR format variant (named or SID). + * @param[out] lydctx_p Pointer to the created CBOR parser context. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, + uint32_t val_opts, struct lyd_cbor_ctx **lydctx_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx = NULL; + + assert(lydctx_p); + + /* Initialize context with calloc to ensure all fields are zero */ + lydctx = calloc(1, sizeof *lydctx); + LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM); + lydctx->parse_opts = parse_opts; + lydctx->val_opts = val_opts; + lydctx->free = lyd_cbor_ctx_free; + + /* Create low-level CBOR context */ + LY_CHECK_GOTO(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), cleanup); + + *lydctx_p = lydctx; + return ret; + +cleanup: + if (lydctx) + { + lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); + } + return ret; +} + +LY_ERR +lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) +{ + LY_ERR ret = LY_SUCCESS; + struct lyd_cbor_ctx *lydctx = NULL; + cbor_item_t *cbor_data = NULL; + struct cbor_load_result result = {0}; + + /* Initialize context */ + LY_CHECK_GOTO(ret = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, &lydctx), cleanup); + + lydctx->int_opts = int_opts; + lydctx->ext = ext; + + /* + * Loads CBOR data from the current input buffer. + * + * Parameters: + * in->current - Pointer to the current position in the input buffer. + * in->length - Length of the data to be loaded. + * &result - Pointer to a variable where the result status will be stored. + * + * Returns: + * cbor_data - Pointer to the loaded CBOR data structure, or NULL on failure. + */ + /* need to convert in->current from const char* to cbor_data type */ + cbor_data = cbor_load(in->current, in->length, &result); + lydctx->cborctx->cbor_data = cbor_data; + + if (!cbor_data) + { + LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: no data returned from cbor_load()."); + ret = LY_EVALID; + goto cleanup; + } + if (result.error.code != CBOR_ERR_NONE) + { + LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: parsing error (code %d).", result.error.code); + ret = LY_EVALID; + goto cleanup; + } + + /* Probably need to check if the obtained data is a operational node and + then write functions to parse them accordingly. If not then continue below */ + + /* Parse the CBOR structure */ + ret = lydcbor_parse_subtree(lydctx, parent, first_p, parsed, cbor_data); + +cleanup: + if (cbor_data) + { + cbor_decref(&cbor_data); + } + + if (ret) + { + if (lydctx) + { lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); lydctx = NULL; } diff --git a/src/parser_cbor.h b/src/parser_cbor.h deleted file mode 100644 index 5b6b0dc94..000000000 --- a/src/parser_cbor.h +++ /dev/null @@ -1,279 +0,0 @@ -/** - * @file parser_cbor.h - * @author - * @brief CBOR data parser for libyang - * - * Copyright (c) 2020 - 2023 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_PARSER_CBOR_H_ -#define LY_PARSER_CBOR_H_ - -#ifdef ENABLE_CBOR_SUPPORT - -#include -#include -#include - -#include "log.h" -#include "tree_data.h" -#include "parser_internal.h" -#include "set.h" - -struct ly_ctx; -struct ly_in; -struct ly_out; -struct lyd_ctx; -struct lyd_node; -struct ly_set; -struct lysc_ext_instance; -struct lysc_node; - -/** - * @brief CBOR format variants for different encoding schemes - */ -enum lyd_cbor_format -{ - LYD_CBOR_NAMED, /**< CBOR with named identifiers (JSON-like) */ - LYD_CBOR_SID /**< CBOR with Schema Item identifiers (future implementation) */ -}; - -struct lycbor_ctx { - const struct ly_ctx *ctx; /**< libyang context */ - struct ly_in *in; /**< input structure */ - cbor_item_t *cbor_data; /**< parsed CBOR data */ - enum lyd_cbor_format format; /**< CBOR format variant */ - uint32_t parse_opts; /**< parser options */ - uint32_t val_opts; /**< validation options */ -}; - -/** - * @brief Internal context for CBOR YANG data parser. - * - * This structure extends the basic lyd_ctx pattern used throughout libyang - * and provides CBOR-specific parsing state and configuration. - */ -struct lyd_cbor_ctx -{ - const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ - uint32_t parse_opts; /**< various @ref dataparseroptions. */ - uint32_t val_opts; /**< various @ref datavalidationoptions. */ - uint32_t int_opts; /**< internal parser options */ - uint32_t path_len; /**< used bytes in the path buffer */ - char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */ - struct ly_set node_when; /**< set of nodes with "when" conditions */ - struct ly_set node_types; /**< set of nodes with unresolved types */ - struct ly_set meta_types; /**< set of metadata with unresolved types */ - struct ly_set ext_node; /**< set of nodes with extension instances to validate */ - struct ly_set ext_val; /**< set of nested extension data to validate */ - struct lyd_node *op_node; /**< if an operation is being parsed, its node */ - const struct lys_module *val_getnext_ht_mod; - struct ly_ht *val_getnext_ht; - - /* callbacks */ - lyd_ctx_free_clb free; /**< destructor */ - - struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ - - /* CBOR-specific members */ - enum lyd_cbor_format format; /**< CBOR format being parsed */ -}; - -/** - * @brief Create new CBOR context for parsing. - * - * @param[in] ctx libyang context. - * @param[in] in Input handler. - * @param[out] cbor_ctx_p Pointer to store the created CBOR context. - * @return LY_ERR value. - */ -LY_ERR -lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p); - -/** - * @brief Free CBOR context. - * - * @param[in] cbor_ctx CBOR context to free. - */ -void -lycbor_ctx_free(struct lycbor_ctx *cbor_ctx); - -/** - * @brief Parse CBOR data into libyang data tree. - * - * This function mirrors the signature and behavior of lyd_parse_json() but handles - * CBOR input instead. It supports both named identifier and SID formats. - * - * @param[in] ctx libyang context. - * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance - * @param[in] parent Parent to connect the parsed nodes to, if any. - * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL. - * @param[in] in Input structure to read from. - * @param[in] parse_opts Options for parser, see @ref dataparseroptions. - * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. - * @param[in] int_opts Internal data parser options. - * @param[out] parsed Set to add all the parsed siblings into. - * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in. - * @param[out] lydctx_p Data parser context to finish validation. - * @return LY_ERR value. - */ -LY_ERR lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, - struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p); - -/** - * @brief Parse CBOR data from memory into libyang data tree. - * - * Convenience function for parsing CBOR data directly from memory buffer. - * - * @param[in] ctx libyang context. - * @param[in] data CBOR data to parse. - * @param[in] data_len Length of @p data. - * @param[in] format CBOR format variant (named or SID). - * @param[in] parse_opts Parse options, see @ref dataparseroptions. - * @param[in] val_opts Validation options, see @ref datavalidationoptions. - * @param[out] tree Parsed data tree. - * @return LY_ERR value. - */ -LIBYANG_API_DECL LY_ERR lyd_parse_cbor_data(const struct ly_ctx *ctx, const char *data, size_t data_len, - enum lyd_cbor_format format, uint32_t parse_opts, uint32_t val_opts, struct lyd_node **tree); - -/** - * @brief Print libyang data tree as CBOR. - * - * @param[in] root Root node of the data tree to print. - * @param[in] format CBOR format variant to use for output. - * @param[in] out Output structure to write to. - * @param[in] options Print options. - * @return LY_ERR value. - */ -LIBYANG_API_DECL LY_ERR lyd_print_cbor_data(const struct lyd_node *root, enum lyd_cbor_format format, - struct ly_out *out, uint32_t options); - -/* Internal functions (used by parser_data.c and other libyang components) */ - -/** - * @brief Detect CBOR format variant from input data. - * - * @param[in] in Input structure to analyze. - * @param[out] format Detected format. - * @return LY_ERR value. - */ -LY_ERR lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format); - -/** - * @brief Parse a single CBOR value according to schema node. - * - * @param[in] lydctx CBOR parser context. - * @param[in] snode Schema node for the value. - * @param[in] cbor_item CBOR item to parse. - * @param[out] node Created data node. - * @return LY_ERR value. - */ -LY_ERR lydcbor_parse_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const void *cbor_item, struct lyd_node **node); - -/** - * @brief Parse CBOR metadata/attributes. - * - * @param[in] lydctx CBOR parser context. - * @param[in] cbor_item CBOR item containing metadata. - * @param[in,out] node Data node to attach metadata to. - * @return LY_ERR value. - */ -LY_ERR lydcbor_parse_metadata(struct lyd_cbor_ctx *lydctx, const void *cbor_item, struct lyd_node *node); - -/** - * @brief Create a new CBOR parser context. - * - * @param[in] ctx libyang context. - * @param[in] ext Extension instance providing context for the top level element, NULL if none. - * @param[in] parse_opts Parse options, see @ref dataparseroptions. - * @param[in] val_opts Validation options, see @ref datavalidationoptions. - * @param[in] format CBOR format variant (named or SID). - * @param[out] lydctx_p Pointer to the created CBOR parser context. - * @return LY_ERR value. - */ -LY_ERR lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, - uint32_t parse_opts, uint32_t val_opts, enum lyd_cbor_format format, - struct lyd_cbor_ctx **lydctx_p); - - /* Add these declarations to parser_cbor.h or at the top of parser_cbor.c */ - -static LY_ERR -lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed); - - -static LY_ERR -lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed); - -/** - * @brief Parse a CBOR container recursive - */ -static LY_ERR -lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); -static LY_ERR -lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); - -static LY_ERR -lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); - - -static LY_ERR -lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); - -static LY_ERR -lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed); -static LY_ERR -lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - struct lyd_node **node, const cbor_item_t *cbor_value); - - -/** - * @brief Get schema node from CBOR node name, following lydjson_get_snode logic. - * - * @param[in] lydctx CBOR parser context. - * @param[in] name Node name. - * @param[in] name_len Length of node name. - * @param[in] parent Data parent node. - * @param[out] snode Schema node found. - * @param[out] ext Extension instance if found. - * @return LY_ERR value. - */ -static LY_ERR -lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, - struct lyd_node *parent, const struct lysc_node **snode, - const struct lysc_ext_instance **ext); - - -/** - * @brief Get module prefix from a qualified name. - * - * @param[in] qname Qualified name (prefix:name or just name). - * @param[in] qname_len Length of the qualified name. - * @param[out] prefix Extracted prefix (points into qname, not allocated). - * @param[out] prefix_len Length of the prefix. - * @param[out] name Local name (points into qname, not allocated). - * @param[out] name_len Length of the local name. - * @return LY_SUCCESS on success. - */ -static LY_ERR -lydcbor_parse_qname(const char *qname, size_t qname_len, const char **prefix, size_t *prefix_len, - const char **name, size_t *name_len); - -#endif /* ENABLE_CBOR_SUPPORT */ - -#endif /* LY_PARSER_CBOR_H_ */ \ No newline at end of file diff --git a/src/parser_internal.h b/src/parser_internal.h index 8174de894..4ce7ba68e 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -175,6 +175,34 @@ struct lyd_lyb_ctx { }; }; +#ifdef ENABLE_CBOR_SUPPORT +/** + * @brief Internal context for CBOR data parser. + */ +struct lyd_cbor_ctx +{ + const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ + uint32_t parse_opts; /**< various @ref dataparseroptions. */ + uint32_t val_opts; /**< various @ref datavalidationoptions. */ + uint32_t int_opts; /**< internal parser options */ + uint32_t path_len; /**< used bytes in the path buffer */ + char path[LYD_PARSER_BUFSIZE]; /**< buffer for the generated path */ + struct ly_set node_when; /**< set of nodes with "when" conditions */ + struct ly_set node_types; /**< set of nodes with unresolved types */ + struct ly_set meta_types; /**< set of metadata with unresolved types */ + struct ly_set ext_node; /**< set of nodes with extension instances to validate */ + struct ly_set ext_val; /**< set of nested extension data to validate */ + struct lyd_node *op_node; /**< if an operation is being parsed, its node */ + const struct lys_module *val_getnext_ht_mod; + struct ly_ht *val_getnext_ht; + + /* callbacks */ + lyd_ctx_free_clb free; /**< destructor */ + + struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ +}; +#endif /* ENABLE_CBOR_SUPPORT */ + /** * @brief Parsed extension instance data to validate. */ @@ -325,6 +353,32 @@ LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, struct lyd_ctx **lydctx_p); +#ifdef ENABLE_CBOR_SUPPORT +/** + * @brief Parse CBOR data into libyang data tree. + * + * This function mirrors the signature and behavior of lyd_parse_json() but handles + * CBOR input instead. It supports both named identifier and SID formats. + * + * @param[in] ctx libyang context. + * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance + * @param[in] parent Parent to connect the parsed nodes to, if any. + * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL. + * @param[in] in Input structure to read from. + * @param[in] parse_opts Options for parser, see @ref dataparseroptions. + * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. + * @param[in] int_opts Internal data parser options. + * @param[out] parsed Set to add all the parsed siblings into. + * @param[out] subtree_sibling Set if ::LYD_PARSE_SUBTREE is used and another subtree is following in @p in. + * @param[out] lydctx_p Data parser context to finish validation. + * @return LY_ERR value. + */ +LY_ERR lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, + struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p); + +#endif /* ENABLE_CBOR_SUPPORT */ + /** * @brief Validate eventTime date-and-time value. * diff --git a/src/tree_data.c b/src/tree_data.c index 1156eae67..d87e389e3 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -35,7 +35,6 @@ #include "in_internal.h" #include "log.h" #include "ly_common.h" -#include "parser_cbor.h" #include "parser_data.h" #include "parser_internal.h" #include "path.h" From c861b17d192df81d7f134e001a70b829aa1f97a7 Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Fri, 25 Jul 2025 11:07:59 +0530 Subject: [PATCH 3/8] feat: add CBOR printer skeleton and rename parser functions --- CMakeLists.txt | 8 +- src/parser_cbor.c | 131 ++++++- src/parser_data.h | 5 + src/printer_cbor.c | 782 +++++++++++++++++++++++++++++++++++++++++ src/printer_data.c | 2 +- src/printer_internal.h | 10 + 6 files changed, 920 insertions(+), 18 deletions(-) create mode 100644 src/printer_cbor.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b12102fa..a2d2a333a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,10 +334,10 @@ if(ENABLE_CBOR_SUPPORT) add_definitions(-DENABLE_CBOR_SUPPORT) include_directories(${LIBCBOR_INCLUDE_DIRS}) # Add CBOR parser files to the library sources - list(APPEND libsrc src/parser_cbor.c src/lcbor.c) + list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) list(APPEND headers src/lcbor.h) # Add CBOR files to format sources - list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c) + list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) else() message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") endif() @@ -351,10 +351,10 @@ if(ENABLE_CBOR_SUPPORT) include_directories(${LIBCBOR_INCLUDE_DIR}) set(LIBCBOR_LIBRARIES ${LIBCBOR_LIBRARY}) # Add CBOR parser files to the library sources - list(APPEND libsrc src/parser_cbor.c src/lcbor.c) + list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) list(APPEND headers src/lcbor.h) # Add CBOR files to format sources - list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c) + list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) else() message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") endif() diff --git a/src/parser_cbor.c b/src/parser_cbor.c index bcc12d2aa..1362c6033 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -41,7 +41,6 @@ #include #include - void print_json(cbor_item_t *item); void print_json_string(const cbor_item_t *item) @@ -51,6 +50,22 @@ void print_json_string(const cbor_item_t *item) printf("\"%.*s\"", (int)length, str); } +void print_json_array(const cbor_item_t *item) +{ + printf("["); + size_t size = cbor_array_size(item); + cbor_item_t **handle = cbor_array_handle(item); + + for (size_t i = 0; i < size; ++i) + { + print_json(handle[i]); + if (i < size - 1) + printf(", "); + } + + printf("]"); +} + void print_json_map(const cbor_item_t *item) { printf("{"); @@ -69,36 +84,92 @@ void print_json_map(const cbor_item_t *item) printf("}"); } +void print_json_number(const cbor_item_t *item) +{ + if (cbor_isa_uint(item)) { + printf("%lu", cbor_get_uint64(item)); + } else if (cbor_isa_negint(item)) { + printf("-%lu", cbor_get_uint64(item) + 1); + } else if (cbor_isa_float_ctrl(item)) { + if (cbor_float_get_width(item) == CBOR_FLOAT_64) { + printf("%f", cbor_float_get_float8(item)); + } else if (cbor_float_get_width(item) == CBOR_FLOAT_32) { + printf("%f", cbor_float_get_float4(item)); + } else if (cbor_float_get_width(item) == CBOR_FLOAT_16) { + printf("%f", cbor_float_get_float2(item)); + } + } +} + void print_json_bool(const cbor_item_t *item) { printf(cbor_is_bool(item) && cbor_ctrl_value(item) ? "true" : "false"); } +void print_json_null(const cbor_item_t *item) +{ + printf("null"); +} + void print_json(cbor_item_t *item) { + if (!item) { + printf("null"); + return; + } + if (cbor_isa_map(item)) { print_json_map(item); } + else if (cbor_isa_array(item)) + { + print_json_array(item); + } else if (cbor_isa_string(item)) { print_json_string(item); } + else if (cbor_isa_uint(item) || cbor_isa_negint(item)) + { + print_json_number(item); + } + else if (cbor_isa_float_ctrl(item)) + { + // Check if it's a control value (null, undefined, true, false) + if (cbor_float_get_width(item) == CBOR_FLOAT_0) { + uint8_t ctrl = cbor_ctrl_value(item); + if (ctrl == 20) { + printf("false"); + } else if (ctrl == 21) { + printf("true"); + } else if (ctrl == 22) { + printf("null"); + } else if (ctrl == 23) { + printf("undefined"); + } else { + printf("null"); // unknown control value + } + } else { + print_json_number(item); + } + } else if (cbor_is_bool(item)) { print_json_bool(item); } else { - printf("null"); // fallback for unsupported types + printf("null"); // fallback for truly unsupported types } } -static LY_ERR lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, +static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); /** - * @brief Free the CBOR parser context + * @brief Free the CBOR data parser context + * CBOR implementation of lyd_ctx_free_clb(). * * @param[in] lydctx Data parser context to free. */ @@ -372,6 +443,7 @@ lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snod return LY_EVALID; } + lyd_hash(node); /* Insert into tree */ ret = lyd_insert_sibling(*first_p, node, first_p); if (ret) @@ -400,6 +472,7 @@ lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sno return LY_EVALID; } + lyd_hash(node); /* Insert into tree first */ ret = lyd_insert_sibling(*first_p, node, first_p); if (ret) @@ -416,7 +489,7 @@ lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sno if (cbor_isa_map(cbor_value) && cbor_map_size(cbor_value) > 0) { struct lyd_node *child_first = NULL; - ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, cbor_value); + ret = lydcbor_subtree_r(lydctx, node, &child_first, parsed, cbor_value); if (ret) { return ret; @@ -432,6 +505,27 @@ lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sno return LY_SUCCESS; } +/* Helper function to check if CBOR item is null/undefined */ +static ly_bool +lydcbor_is_null(const cbor_item_t *item) +{ + if (!item) { + return 1; + } + + /* Check for CBOR null primitive */ + if (cbor_isa_float_ctrl(item)) { + if (cbor_float_get_width(item) == CBOR_FLOAT_0) { + uint8_t ctrl = cbor_ctrl_value(item); + if (ctrl == 22 || ctrl == 23) { /* null or undefined */ + return 1; + } + } + } + + return 0; +} + static LY_ERR lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) @@ -491,7 +585,7 @@ lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sn ret = LY_EVALID; goto cleanup; } - + lyd_hash(node); /* Insert the list node */ ret = lyd_insert_sibling(*first_p, node, first_p); LY_CHECK_GOTO(ret, cleanup); @@ -501,7 +595,7 @@ lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *sn /* Parse list entry content */ struct lyd_node *child_first = NULL; - ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, item); + ret = lydcbor_subtree_r(lydctx, node, &child_first, parsed, item); LY_CHECK_GOTO(ret, cleanup); /* Link children to list entry */ @@ -529,7 +623,6 @@ lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, if (cbor_isa_array(cbor_value)) { - /* Array of list entries */ ret = lydcbor_parse_list_array(lydctx, snode, cbor_value, first_p, parsed); } else if (cbor_isa_map(cbor_value)) @@ -540,6 +633,7 @@ lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, ret = lyd_create_inner(snode, &node); LY_CHECK_RET(ret); + lyd_hash(node); /* Insert into tree */ ret = lyd_insert_sibling(*first_p, node, first_p); if (ret) @@ -554,7 +648,7 @@ lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, /* Parse list entry content */ struct lyd_node *child_first = NULL; - ret = lydcbor_parse_subtree(lydctx, node, &child_first, parsed, cbor_value); + ret = lydcbor_subtree_r(lydctx, node, &child_first, parsed, cbor_value); if (ret) { return ret; @@ -591,6 +685,7 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, return LY_EVALID; } + lyd_hash(node); /* Insert into tree */ ret = lyd_insert_sibling(*first_p, node, first_p); if (ret) @@ -927,7 +1022,8 @@ lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node ret = LY_EVALID; goto cleanup; } - + + lyd_hash(node); /* Insert the node */ ret = lyd_insert_sibling(*first_p, node, first_p); LY_CHECK_GOTO(ret, cleanup); @@ -946,12 +1042,12 @@ lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node } static LY_ERR -lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, +lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj) { LY_ERR ret = LY_SUCCESS; - printf("Entering lydcbor_parse_subtree\n"); + printf("Entering lydcbor_subtree_r\n"); printf("CBOR object:\n"); print_json(cbor_obj); printf("\n"); @@ -992,6 +1088,11 @@ lydcbor_parse_subtree(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, goto cleanup; } + if (lydcbor_is_null(value_item)) { + // Skip null values - don't create any nodes + continue; // or return LY_SUCCESS depending on your loop structure + } + /* Get key string */ LY_CHECK_GOTO(ret = lydcbor_get_key_string(lydctx, key_item, &key_str, &key_len), cleanup); @@ -1099,6 +1200,10 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st lydctx->int_opts = int_opts; lydctx->ext = ext; + /* find the operation node if it exists already */ + LY_CHECK_GOTO(ret = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); + + /* * Loads CBOR data from the current input buffer. * @@ -1131,7 +1236,7 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st then write functions to parse them accordingly. If not then continue below */ /* Parse the CBOR structure */ - ret = lydcbor_parse_subtree(lydctx, parent, first_p, parsed, cbor_data); + ret = lydcbor_subtree_r(lydctx, parent, first_p, parsed, cbor_data); cleanup: if (cbor_data) diff --git a/src/parser_data.h b/src/parser_data.h index a41ec77b8..accfff976 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -40,6 +40,11 @@ struct ly_in; * can be found in [RFC 7951](http://tools.ietf.org/html/rfc7951). The specification does not cover RPCs, actions and * Notifications, so the representation of these data trees is proprietary and corresponds to the representation of these * trees in XML. + * + * - CBOR + * + * The reference documentation would be `Encoding of Data Modeled with YANG in the Concise Binary Object + * Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254) * * While the parsers themselves process the input data only syntactically, all the parser functions actually incorporate * the [common validator](@ref howtoDataValidation) checking the input data semantically. Therefore, the parser functions diff --git a/src/printer_cbor.c b/src/printer_cbor.c new file mode 100644 index 000000000..d0cc0997a --- /dev/null +++ b/src/printer_cbor.c @@ -0,0 +1,782 @@ +/** + * @file printer_cbor.c + * @author Meher Rushi + * @brief CBOR printer for libyang data tree using libcbor + * + * Copyright (c) 2024 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 "printer_data.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "context.h" +#include "log.h" +#include "ly_common.h" +#include "out.h" +#include "plugins_exts.h" +#include "printer_internal.h" +#include "set.h" +#include "tree_data.h" +#include "tree_schema.h" + +/** + * @brief CBOR printer context + */ +struct cborpr_ctx { + struct ly_out *out; /**< output structure */ + const struct lyd_node *root; /**< root node of the subtree being printed */ + const struct lyd_node *print_sibling_metadata; /**< node with metadata supposed to be printed */ + ly_bool simple_status; /**< flag for simple status */ + + uint16_t level; /**< current nesting level */ + uint32_t options; /**< [printer flags](@ref dataprinterflags) */ + const struct ly_ctx *ctx; /**< libyang context */ + + struct ly_set prefix; /**< printed module prefixes */ + uint32_t array_index; /**< index in array if we are printing an array element */ + + cbor_item_t *root_item; /**< root CBOR item */ +}; + +/** + * @brief Check if module needs prefix - ROBUST VERSION + */ +static ly_bool +cbor_module_needs_prefix(struct cborpr_ctx *ctx, const struct lys_module *module) +{ + /* CRITICAL: Add comprehensive null checks */ + if (!ctx) { + fprintf(stderr, "DEBUG: cbor_module_needs_prefix called with NULL ctx\n"); + return 0; + } + + if (!module) { + fprintf(stderr, "DEBUG: cbor_module_needs_prefix called with NULL module\n"); + return 0; + } + + if (!module->name) { + fprintf(stderr, "DEBUG: Module has NULL name\n"); + return 0; + } + + fprintf(stderr, "DEBUG: Checking prefix for module: '%s'\n", module->name); + + /* Always add prefix if explicitly requested */ + if (ctx->options & LYD_PRINT_WD_ALL_TAG) { + fprintf(stderr, "DEBUG: Prefix requested via options\n"); + return 1; + } + + /* Check if it's an internal libyang module */ + if (!strcmp(module->name, "ietf-yang-metadata") || + !strcmp(module->name, "yang") || + !strcmp(module->name, "ietf-inet-types") || + !strcmp(module->name, "ietf-yang-types") || + !strcmp(module->name, "ietf-yang-structure-ext")) { + fprintf(stderr, "DEBUG: Internal module, no prefix needed\n"); + return 0; + } + + /* For now, don't add prefixes unless explicitly requested */ + fprintf(stderr, "DEBUG: No prefix needed for module: '%s'\n", module->name); + return 0; +} + + +/** + * @brief Safe wrapper for cbor_build_string that handles NULL inputs - ENHANCED + */ +static cbor_item_t * +safe_cbor_build_string(const char *str) +{ + if (!str) { + fprintf(stderr, "DEBUG: NULL string passed to safe_cbor_build_string, using empty string\n"); + return cbor_build_string(""); + } + + fprintf(stderr, "DEBUG: Building CBOR string: '%s' (len=%zu)\n", str, strlen(str)); + cbor_item_t *item = cbor_build_string(str); + if (!item) { + fprintf(stderr, "DEBUG: cbor_build_string failed for '%s'\n", str); + } + return item; +} + +/** + * @brief Convert YANG value to CBOR item + */ +static cbor_item_t * +cbor_value_to_item(const struct lyd_node *node) +{ + const char *str; + cbor_item_t *item = NULL; + + if (!node) { + fprintf(stderr, "DEBUG: cbor_value_to_item called with NULL node\n"); + return safe_cbor_build_string(""); + } + + fprintf(stderr, "DEBUG: cbor_value_to_item - node type: %d\n", node->schema->nodetype); + + if (!(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) { + fprintf(stderr, "DEBUG: Non-leaf node, returning empty string\n"); + return safe_cbor_build_string(""); /* Empty string for non-leaf nodes */ + } + + str = lyd_get_value(node); + fprintf(stderr, "DEBUG: Node value: '%s'\n", str ? str : "NULL"); + + if (!str || strlen(str) == 0) { + fprintf(stderr, "DEBUG: Empty or NULL value, returning empty string\n"); + return safe_cbor_build_string(""); + } + + /* FIXED: Add null check for schema before casting */ + if (!node->schema) { + fprintf(stderr, "DEBUG: Node has NULL schema, using string value\n"); + return cbor_build_string(str); + } + + /* Handle different data types based on YANG type */ + switch (((struct lysc_node_leaf *)node->schema)->type->basetype) { + case LY_TYPE_BOOL: + if (strcmp(str, "true") == 0) { + item = cbor_build_bool(true); + } else { + item = cbor_build_bool(false); + } + break; + + case LY_TYPE_INT8: + case LY_TYPE_INT16: + case LY_TYPE_INT32: + case LY_TYPE_INT64: { + char *endptr; + long long num = strtoll(str, &endptr, 10); + if (*endptr == '\0') { + if (num >= 0) { + item = cbor_build_uint64((uint64_t)num); + } else { + item = cbor_build_negint64((uint64_t)(-num - 1)); + } + } else { + item = cbor_build_string(str); + } + break; + } + + case LY_TYPE_UINT8: + case LY_TYPE_UINT16: + case LY_TYPE_UINT32: + case LY_TYPE_UINT64: { + char *endptr; + unsigned long long num = strtoull(str, &endptr, 10); + if (*endptr == '\0') { + item = cbor_build_uint64(num); + } else { + item = cbor_build_string(str); + } + break; + } + + case LY_TYPE_DEC64: { + char *endptr; + double num = strtod(str, &endptr); + if (*endptr == '\0') { + item = cbor_build_float8(num); + } else { + item = cbor_build_string(str); + } + break; + } + + case LY_TYPE_EMPTY: + item = cbor_build_string(""); + break; + + default: + /* String types and others */ + item = cbor_build_string(str); + break; + } + + return item; +} + +/** + * @brief Count direct children of a node + */ +static size_t +cbor_count_children(const struct lyd_node *node) +{ + size_t count = 0; + const struct lyd_node *child; + + LY_LIST_FOR(lyd_child(node), child) { + count++; + } + return count; +} + +/** + * @brief Count sibling nodes with the same name (for leaf-lists) + */ +static size_t +cbor_count_siblings_same_name(const struct lyd_node *node) +{ + size_t count = 0; + const struct lyd_node *sibling; + const struct lysc_node *schema = node->schema; + + /* Check if this is a leaf-list */ + if (!(schema->nodetype & LYS_LEAFLIST)) { + return 1; + } + + /* Count siblings with same schema */ + LY_LIST_FOR(node, sibling) { + if (sibling->schema == schema) { + count++; + } else { + break; /* leaf-list instances are consecutive */ + } + } + + return count; +} + +/** + * @brief Print a single node recursively + */ +static LY_ERR cbor_print_node(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map); + +/** + * @brief Print container or list node - EXTRA SAFE VERSION + */ +static LY_ERR +cbor_print_container(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + cbor_item_t *node_map = NULL; + cbor_item_t *key_item = NULL; + char *node_name = NULL; + const struct lyd_node *child; + LY_ERR ret = LY_SUCCESS; + size_t child_count; + + fprintf(stderr, "DEBUG: cbor_print_container called for node: %s\n", + node && node->schema && node->schema->name ? node->schema->name : "NULL"); + + /* COMPREHENSIVE NULL CHECKS */ + if (!ctx) { + fprintf(stderr, "DEBUG: Container called with NULL ctx\n"); + return LY_EINVAL; + } + + if (!node) { + fprintf(stderr, "DEBUG: Container called with NULL node\n"); + return LY_EINVAL; + } + + if (!node->schema) { + fprintf(stderr, "DEBUG: Container node has NULL schema\n"); + return LY_EINVAL; + } + + if (!parent_map) { + fprintf(stderr, "DEBUG: Container called with NULL parent_map\n"); + return LY_EINVAL; + } + + /* Get node name - This should now be safe */ + // node_name = cbor_get_node_name(ctx, node); + node_name = ""; if (!node_name) { + fprintf(stderr, "DEBUG: Failed to get container node name\n"); + ret = LY_EMEM; + goto cleanup; + } + + fprintf(stderr, "DEBUG: Container name: '%s'\n", node_name); + + /* Count children */ + child_count = cbor_count_children(node); + fprintf(stderr, "DEBUG: Container has %zu children\n", child_count); + + /* Create map for this container/list */ + node_map = cbor_new_definite_map(child_count); + if (!node_map) { + fprintf(stderr, "DEBUG: Failed to create CBOR map for container\n"); + ret = LY_EMEM; + goto cleanup; + } + + /* Add all children to the map */ + LY_LIST_FOR(lyd_child(node), child) { + if (!child || !child->schema || !child->schema->name) { + fprintf(stderr, "DEBUG: Skipping invalid child\n"); + continue; + } + + fprintf(stderr, "DEBUG: Processing child: %s\n", child->schema->name); + ret = cbor_print_node(ctx, child, node_map); + if (ret != LY_SUCCESS) { + fprintf(stderr, "DEBUG: Failed to process child node\n"); + goto cleanup; + } + } + + /* Add this container/list to parent map */ + key_item = safe_cbor_build_string(node_name); + if (!key_item) { + fprintf(stderr, "DEBUG: Failed to create key item for container\n"); + ret = LY_EMEM; + goto cleanup; + } + + if (!cbor_map_add(parent_map, (struct cbor_pair) { + .key = key_item, + .value = node_map + })) { + fprintf(stderr, "DEBUG: Failed to add container to parent map\n"); + ret = LY_EMEM; + goto cleanup; + } + + fprintf(stderr, "DEBUG: Container added successfully\n"); + + /* Items are now owned by the map, don't decref them */ + key_item = NULL; + node_map = NULL; + +cleanup: + if (key_item) { + cbor_decref(&key_item); + } + if (node_map) { + cbor_decref(&node_map); + } + if (node_name) { + free(node_name); + } + return ret; +} + +/** + * @brief Print leaf or leaf-list node + */ +static LY_ERR +cbor_print_leaf(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + cbor_item_t *key_item = NULL; + cbor_item_t *value_item = NULL; + cbor_item_t *array_item = NULL; + char *node_name = NULL; + const struct lyd_node *sibling; + LY_ERR ret = LY_SUCCESS; + size_t sibling_count; + + fprintf(stderr, "DEBUG: cbor_print_leaf called for node: %s\n", + node && node->schema && node->schema->name ? node->schema->name : "NULL"); + + /* FIXED: Add null checks */ + if (!node || !node->schema) { + fprintf(stderr, "DEBUG: Leaf node or schema is NULL\n"); + return LY_EINVAL; + } + + /* Get node name */ + // node_name = cbor_get_node_name(ctx, node); + node_name = ""; + if (!node_name) { + fprintf(stderr, "DEBUG: Failed to get node name\n"); + ret = LY_EMEM; + goto cleanup; + } + + fprintf(stderr, "DEBUG: Got node name: '%s'\n", node_name); + + /* Check if this is a leaf-list with multiple values */ + sibling_count = cbor_count_siblings_same_name(node); + fprintf(stderr, "DEBUG: Sibling count: %zu\n", sibling_count); + + if (sibling_count > 1 && (node->schema->nodetype & LYS_LEAFLIST)) { + fprintf(stderr, "DEBUG: Processing leaf-list with %zu values\n", sibling_count); + + /* Create array for leaf-list */ + array_item = cbor_new_definite_array(sibling_count); + if (!array_item) { + fprintf(stderr, "DEBUG: Failed to create CBOR array\n"); + ret = LY_EMEM; + goto cleanup; + } + + /* Add all values to array */ + LY_LIST_FOR(node, sibling) { + if (sibling->schema != node->schema) { + break; /* Different schema, stop */ + } + + fprintf(stderr, "DEBUG: Adding leaf-list value to array\n"); + value_item = cbor_value_to_item(sibling); + if (!value_item) { + fprintf(stderr, "DEBUG: Failed to create CBOR value item\n"); + ret = LY_EMEM; + goto cleanup; + } + + if (!cbor_array_push(array_item, value_item)) { + fprintf(stderr, "DEBUG: Failed to add item to CBOR array\n"); + cbor_decref(&value_item); + ret = LY_EMEM; + goto cleanup; + } + + value_item = NULL; /* Array owns it now */ + } + + /* Add array to parent map */ + key_item = safe_cbor_build_string(node_name); + if (!key_item) { + fprintf(stderr, "DEBUG: Failed to create CBOR key item for leaf-list\n"); + ret = LY_EMEM; + goto cleanup; + } + + if (!cbor_map_add(parent_map, (struct cbor_pair) { + .key = key_item, + .value = array_item + })) { + fprintf(stderr, "DEBUG: Failed to add leaf-list to parent map\n"); + ret = LY_EMEM; + goto cleanup; + } + + /* Items are now owned by the map */ + key_item = NULL; + array_item = NULL; + + } else { + fprintf(stderr, "DEBUG: Processing single leaf value\n"); + + /* Single leaf value */ + value_item = cbor_value_to_item(node); + if (!value_item) { + fprintf(stderr, "DEBUG: Failed to create CBOR value item for leaf\n"); + ret = LY_EMEM; + goto cleanup; + } + + key_item = safe_cbor_build_string(node_name); + if (!key_item) { + fprintf(stderr, "DEBUG: Failed to create CBOR key item for leaf\n"); + ret = LY_EMEM; + goto cleanup; + } + + if (!cbor_map_add(parent_map, (struct cbor_pair) { + .key = key_item, + .value = value_item + })) { + fprintf(stderr, "DEBUG: Failed to add leaf to parent map\n"); + ret = LY_EMEM; + goto cleanup; + } + + /* Items are now owned by the map */ + key_item = NULL; + value_item = NULL; + } + + fprintf(stderr, "DEBUG: cbor_print_leaf completed successfully\n"); + +cleanup: + if (key_item) { + cbor_decref(&key_item); + } + if (value_item) { + cbor_decref(&value_item); + } + if (array_item) { + cbor_decref(&array_item); + } + free(node_name); + return ret; +} + +/** + * @brief Print anydata/anyxml node + */ +static LY_ERR +cbor_print_any(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + cbor_item_t *key_item = NULL; + cbor_item_t *value_item = NULL; + char *node_name = NULL; + LY_ERR ret = LY_SUCCESS; + struct lyd_node_any *any = (struct lyd_node_any *)node; + const char *value_str = ""; + + /* FIXED: Add null checks */ + if (!node || !node->schema) { + fprintf(stderr, "DEBUG: Any node or schema is NULL\n"); + return LY_EINVAL; + } + + /* Get node name */ + // node_name = cbor_get_node_name(ctx, node); + node_name = ""; if (!node_name) { + ret = LY_EMEM; + goto cleanup; + } + + /* Convert anydata to string representation for now */ + /* TODO: Could be enhanced to preserve the actual data format */ + switch (any->value_type) { + case LYD_ANYDATA_STRING: + value_str = any->value.str ? (char *)any->value.str : ""; + break; + case LYD_ANYDATA_DATATREE: + /* For now, just indicate it's a data tree */ + value_str = "[DATA TREE]"; + break; + case LYD_ANYDATA_XML: + value_str = any->value.str ? (char *)any->value.str : ""; + break; + case LYD_ANYDATA_JSON: + value_str = any->value.str ? (char *)any->value.str : ""; + break; + default: + value_str = ""; + break; + } + + value_item = cbor_build_string(value_str); + if (!value_item) { + ret = LY_EMEM; + goto cleanup; + } + + key_item = cbor_build_string(node_name); + if (!key_item) { + ret = LY_EMEM; + goto cleanup; + } + + if (!cbor_map_add(parent_map, (struct cbor_pair) { + .key = key_item, + .value = value_item + })) { + ret = LY_EMEM; + goto cleanup; + } + + /* Items are now owned by the map */ + key_item = NULL; + value_item = NULL; + +cleanup: + if (key_item) { + cbor_decref(&key_item); + } + if (value_item) { + cbor_decref(&value_item); + } + free(node_name); + return ret; +} + +/** + * @brief Print a single node recursively + */ +static LY_ERR +cbor_print_node(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + /* FIXED: Add null checks at the beginning */ + if (!node || !node->schema) { + fprintf(stderr, "DEBUG: cbor_print_node called with NULL node or schema\n"); + return LY_EINVAL; + } + + switch (node->schema->nodetype) { + case LYS_CONTAINER: + case LYS_LIST: + return cbor_print_container(ctx, node, parent_map); + case LYS_LEAF: + case LYS_LEAFLIST: + return cbor_print_leaf(ctx, node, parent_map); + case LYS_ANYXML: + case LYS_ANYDATA: + return cbor_print_any(ctx, node, parent_map); + default: + /* Skip unknown node types */ + fprintf(stderr, "DEBUG: Skipping unknown node type: %d\n", node->schema->nodetype); + return LY_SUCCESS; + } +} + +/** + * @brief Count root level nodes, handling leaf-lists correctly + */ +static size_t +cbor_count_root_nodes(const struct lyd_node *root) +{ + size_t count = 0; + const struct lyd_node *node; + const struct lysc_node *last_schema = NULL; + + LY_LIST_FOR(root, node) { + /* FIXED: Add null check for schema */ + if (!node->schema) { + continue; + } + + /* For leaf-lists, only count the first occurrence */ + if (node->schema != last_schema) { + count++; + last_schema = node->schema; + } else if (!(node->schema->nodetype & LYS_LEAFLIST)) { + count++; + } + } + + return count; +} + +/** + * @brief Main function to print data tree in CBOR format + */ +LY_ERR +cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options) +{ + LY_ERR ret = LY_SUCCESS; + struct cborpr_ctx ctx = {0}; + const struct lyd_node *node; + const struct lysc_node *last_schema = NULL; + size_t root_count; + unsigned char *cbor_data = NULL; + size_t cbor_data_len = 0; + + if (!out) { + return LY_EINVAL; + } + + /* Initialize context */ + ctx.out = out; + ctx.root = root; + ctx.options = options; + ctx.level = 0; + + if (root) { + ctx.ctx = LYD_CTX(root); + + /* Count root level nodes */ + root_count = cbor_count_root_nodes(root); + + /* Debug: Print what we're processing */ + fprintf(stderr, "DEBUG: Processing %zu root nodes\n", root_count); + + /* Create root map */ + ctx.root_item = cbor_new_definite_map(root_count); + if (!ctx.root_item) { + fprintf(stderr, "DEBUG: Failed to create root map\n"); + ret = LY_EMEM; + goto cleanup; + } + + /* Process all root nodes */ + LY_LIST_FOR(root, node) { + /* FIXED: Add null check for schema */ + if (!node->schema) { + fprintf(stderr, "DEBUG: Skipping node with NULL schema\n"); + continue; + } + + /* Skip duplicate leaf-list entries (they are handled together) */ + if ((node->schema->nodetype & LYS_LEAFLIST) && (node->schema == last_schema)) { + continue; + } + + fprintf(stderr, "DEBUG: Processing node: %s\n", node->schema->name); + + ctx.root = node; + ret = cbor_print_node(&ctx, node, ctx.root_item); + if (ret != LY_SUCCESS) { + fprintf(stderr, "DEBUG: Failed to print node: %s\n", node->schema->name); + goto cleanup; + } + + last_schema = node->schema; + + /* Break if not printing siblings */ + if (!(options & LYD_PRINT_WITHSIBLINGS)) { + break; + } + } + } else { + fprintf(stderr, "DEBUG: Empty data tree\n"); + /* Empty data tree - create empty map */ + ctx.root_item = cbor_new_definite_map(0); + if (!ctx.root_item) { + ret = LY_EMEM; + goto cleanup; + } + } + + /* Serialize CBOR to bytes */ + cbor_data_len = cbor_serialize_alloc(ctx.root_item, &cbor_data, &cbor_data_len); + if (cbor_data_len == 0 || !cbor_data) { + fprintf(stderr, "DEBUG: Failed to serialize CBOR or got 0 bytes\n"); + ret = LY_EMEM; + goto cleanup; + } + + fprintf(stderr, "DEBUG: Generated %zu bytes of CBOR data\n", cbor_data_len); + + /* Write to output using ly_print_ macro */ + ly_print_(out, "%.*s", (int)cbor_data_len, cbor_data); + +cleanup: + if (ctx.root_item) { + cbor_decref(&ctx.root_item); + } + if (cbor_data) { + free(cbor_data); + } + + return ret; +} + +/** + * @brief Print data subtree in CBOR format + */ +LY_ERR +cbor_print_tree(struct ly_out *out, const struct lyd_node *root, uint32_t options, size_t max_depth) +{ + /* For now, ignore max_depth and use the regular print function */ + /* TODO: Implement depth limiting if needed */ + (void)max_depth; + return cbor_print_data(out, root, options); +} + +/** + * @brief Print all data trees in CBOR format + */ +LY_ERR +cbor_print_all(struct ly_out *out, const struct lyd_node *root, uint32_t options) +{ + return cbor_print_data(out, root, options); +} \ No newline at end of file diff --git a/src/printer_data.c b/src/printer_data.c index 797b95371..cb1611a4b 100644 --- a/src/printer_data.c +++ b/src/printer_data.c @@ -39,7 +39,7 @@ lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, u ret = lyb_print_data(out, root, options); break; case LYD_CBOR: - // ret = cbor_print_data(out, root, options); + ret = cbor_print_data(out, root, options); break; case LYD_UNKNOWN: LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "Unknown data output format."); diff --git a/src/printer_internal.h b/src/printer_internal.h index 5cf205e77..0f1d8b827 100644 --- a/src/printer_internal.h +++ b/src/printer_internal.h @@ -185,4 +185,14 @@ LY_ERR json_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t */ LY_ERR lyb_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options); +/** + * @brief CBOR printer of YANG data. + * + * @param[in] out Output structure. + * @param[in] root The root element of the (sub)tree to print. + * @param[in] options [Data printer flags](@ref dataprinterflags). + * @return LY_ERR value, number of the printed bytes is updated in ::ly_out.printed. + */ +LY_ERR cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options); + #endif /* LY_PRINTER_INTERNAL_H_ */ From d3f1d6f90c4342e4976d8321ae64e56ee33ed8ff Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Sun, 12 Oct 2025 05:05:27 +0100 Subject: [PATCH 4/8] refactor: align CBOR implementation with project structure --- src/lcbor.c | 100 ++++++++++++++++++++++++++++++++------ src/lcbor.h | 18 ++++--- src/log.h | 1 + src/parser_cbor.c | 109 ++++++++++++++++-------------------------- src/parser_data.h | 3 +- src/parser_internal.h | 1 + 6 files changed, 142 insertions(+), 90 deletions(-) diff --git a/src/lcbor.c b/src/lcbor.c index e3962fa46..e92452eec 100644 --- a/src/lcbor.c +++ b/src/lcbor.c @@ -23,16 +23,71 @@ #include "log.h" #include "ly_common.h" +const char * +lycbor_token2str(enum cbor_type cbortype) +{ + switch (cbortype) { + case CBOR_TYPE_UINT: + return "unsigned integer"; + case CBOR_TYPE_NEGINT: + return "negative integer"; + case CBOR_TYPE_BYTESTRING: + return "byte string"; + case CBOR_TYPE_STRING: + return "string"; + case CBOR_TYPE_ARRAY: + return "array"; + case CBOR_TYPE_MAP: + return "map"; + case CBOR_TYPE_TAG: + return "tag"; + case CBOR_TYPE_FLOAT_CTRL: + return "decimals and special values (true, false, nil, ...)"; + } + + return ""; +} + + return "object"; + case LYJSON_OBJECT_NEXT: + return "object next"; + case LYJSON_OBJECT_CLOSED: + return "object closed"; + case LYJSON_ARRAY: + return "array"; + case LYJSON_ARRAY_NEXT: + return "array next"; + case LYJSON_ARRAY_CLOSED: + return "array closed"; + case LYJSON_OBJECT_NAME: + return "object name"; + case LYJSON_NUMBER: + return "number"; + case LYJSON_STRING: + return "string"; + case LYJSON_TRUE: + return "true"; + case LYJSON_FALSE: + return "false"; + case LYJSON_NULL: + return "null"; + case LYJSON_END: + return "end of input"; + } + + return ""; +} + /** * @brief Free CBOR context. * - * @param[in] cbor_ctx CBOR context to free. + * @param[in] cborctx CBOR context to free. */ -void lycbor_ctx_free(struct lycbor_ctx *cbor_ctx) +void lycbor_ctx_free(struct lycbor_ctx *cborctx) { - if (cbor_ctx) + if (cborctx) { - free(cbor_ctx); + free(cborctx); } } @@ -58,31 +113,48 @@ lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) * * @param[in] ctx libyang context. * @param[in] in Input handler. - * @param[out] cbor_ctx_p Pointer to store the created CBOR context. + * @param[out] cborctx_p Pointer to store the created CBOR context. * @return LY_ERR value. */ LY_ERR -lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p) +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p) { /* TODO : Need to restructure error handling here */ LY_ERR ret = LY_SUCCESS; - struct lycbor_ctx *cbor_ctx; + struct lycbor_ctx *cborctx; + struct cbor_load_result result = {0}; enum lyd_cbor_format format; - assert(ctx && in && cbor_ctx_p); + assert(ctx && in && cborctx_p); /* TODO : error handling after the detect_format function call */ ret = lydcbor_detect_format(in, &format); /* Allocate and initialize CBOR context */ - cbor_ctx = calloc(1, sizeof *cbor_ctx); - LY_CHECK_ERR_RET(!cbor_ctx, LOGMEM(ctx), LY_EMEM); + cborctx = calloc(1, sizeof *cborctx); + LY_CHECK_ERR_RET(!cborctx, LOGMEM(ctx), LY_EMEM); + cborctx->ctx = ctx; + cborctx->in = in; + cborctx->format = format; + + /* load and parse CBOR data */ + cborctx->cbor_data = cbor_load(in->current, in->length, &result); + if (!cborctx->cbor_data) { + LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data."); + free(cborctx); + return LY_EVALID; + } + if (result.error.code != CBOR_ERR_NONE) { + LOGVAL(ctx, LYVE_SYNTAX, "CBOR parsing error (code %d).", result.error.code); + cbor_decref(&cborctx->cbor_data); + free(cborctx); + return LY_EVALID; + } - cbor_ctx->ctx = ctx; - cbor_ctx->in = in; - cbor_ctx->format = format; + /* input line logging */ + ly_log_location(NULL, NULL, NULL, in); - *cbor_ctx_p = cbor_ctx; + *cborctx_p = cborctx; return ret; } diff --git a/src/lcbor.h b/src/lcbor.h index 22d2fb486..838077ca8 100644 --- a/src/lcbor.h +++ b/src/lcbor.h @@ -41,10 +41,16 @@ enum lyd_cbor_format struct lycbor_ctx { const struct ly_ctx *ctx; /**< libyang context */ struct ly_in *in; /**< input structure */ + cbor_item_t *cbor_data; /**< parsed CBOR data */ + enum lyd_cbor_format format; /**< CBOR format variant */ - uint32_t parse_opts; /**< parser options */ - uint32_t val_opts; /**< validation options */ + + struct { + cbor_item_t *cbor_data; /**< parsed CBOR data */ + enum lyd_cbor_format format; /**< CBOR format variant */ + const char *input; + } backup; }; /** @@ -52,19 +58,19 @@ struct lycbor_ctx { * * @param[in] ctx libyang context. * @param[in] in Input handler. - * @param[out] cbor_ctx_p Pointer to store the created CBOR context. + * @param[out] cborctx_p Pointer to store the created CBOR context. * @return LY_ERR value. */ LY_ERR -lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p); +lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p); /** * @brief Free CBOR context. * - * @param[in] cbor_ctx CBOR context to free. + * @param[in] cborctx CBOR context to free. */ void -lycbor_ctx_free(struct lycbor_ctx *cbor_ctx); +lycbor_ctx_free(struct lycbor_ctx *cborctx); #endif /* ENABLE_CBOR_SUPPORT */ diff --git a/src/log.h b/src/log.h index 711b06211..9b0692f75 100644 --- a/src/log.h +++ b/src/log.h @@ -286,6 +286,7 @@ typedef enum { LYVE_SEMANTICS, /**< generic semantic error */ LYVE_SYNTAX_XML, /**< XML-related syntax error */ LYVE_SYNTAX_JSON, /**< JSON-related syntax error */ + LYVE_SYNTAX_CBOR, /**< CBOR-related syntax error */ LYVE_DATA, /**< YANG data does not reflect some of the module restrictions */ LYVE_OTHER /**< Unknown error */ diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 1362c6033..6b7613848 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -1058,15 +1058,6 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, assert(lydctx && first_p && parsed && cbor_obj); - /* assuming that the top level structure is always a map - to be modified to include anything else that it can support */ - - if (!cbor_isa_map(cbor_obj)) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR map"); - return LY_EVALID; - } - size_t map_size = cbor_map_size(cbor_obj); struct cbor_pair *pairs = cbor_map_handle(cbor_obj); @@ -1159,28 +1150,37 @@ lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts uint32_t val_opts, struct lyd_cbor_ctx **lydctx_p) { LY_ERR ret = LY_SUCCESS; - struct lyd_cbor_ctx *lydctx = NULL; + struct lyd_cbor_ctx *lydctx; + enum cbor_type cbortype; assert(lydctx_p); - /* Initialize context with calloc to ensure all fields are zero */ + /* init context */ lydctx = calloc(1, sizeof *lydctx); LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM); lydctx->parse_opts = parse_opts; lydctx->val_opts = val_opts; lydctx->free = lyd_cbor_ctx_free; - /* Create low-level CBOR context */ - LY_CHECK_GOTO(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), cleanup); - - *lydctx_p = lydctx; - return ret; + /* Create low-level CBOR context (includes CBOR parsing) */ + LY_CHECK_ERR_RET(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), free(lydctx), ret); + cbortype = cbor_typeof(lydctx->cborctx->cbor_data); -cleanup: - if (lydctx) + /* assuming that the top level structure is always a map + - though this is not mentioned explicitly in RFC9254 - it is implied + and it is almost always the case - This is a similar assumption made + to the RFC 7951 where JSON Encoding of data modeled by YANG is always assumed + to a have a top-level structure as an object */ + if (!cbor_isa_map(lydctx->cborctx->cbor_data)) { + /* expecting top-level map */ + LOGVAL(ctx, LYVE_SYNTAX_CBOR, "Expected top-level CBOR map, but %s found.", lycbor_token2str(cbortype)); + *lydctx_p = NULL; lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); + return LY_EVALID; } + + *lydctx_p = lydctx; return ret; } @@ -1189,72 +1189,43 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) { - LY_ERR ret = LY_SUCCESS; + LY_ERR r, rc = LY_SUCCESS; struct lyd_cbor_ctx *lydctx = NULL; - cbor_item_t *cbor_data = NULL; - struct cbor_load_result result = {0}; + printf("Entering lyd_parse_cbor\n AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\n"); - /* Initialize context */ - LY_CHECK_GOTO(ret = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, &lydctx), cleanup); + /* Initialize context (CBOR parsing happens in lycbor_ctx_new) */ + rc = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, &lydctx); + LY_CHECK_GOTO(rc, cleanup); lydctx->int_opts = int_opts; lydctx->ext = ext; /* find the operation node if it exists already */ - LY_CHECK_GOTO(ret = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); - - - /* - * Loads CBOR data from the current input buffer. - * - * Parameters: - * in->current - Pointer to the current position in the input buffer. - * in->length - Length of the data to be loaded. - * &result - Pointer to a variable where the result status will be stored. - * - * Returns: - * cbor_data - Pointer to the loaded CBOR data structure, or NULL on failure. - */ - /* need to convert in->current from const char* to cbor_data type */ - cbor_data = cbor_load(in->current, in->length, &result); - lydctx->cborctx->cbor_data = cbor_data; - - if (!cbor_data) - { - LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: no data returned from cbor_load()."); - ret = LY_EVALID; - goto cleanup; - } - if (result.error.code != CBOR_ERR_NONE) - { - LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: parsing error (code %d).", result.error.code); - ret = LY_EVALID; - goto cleanup; - } + LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); - /* Probably need to check if the obtained data is a operational node and - then write functions to parse them accordingly. If not then continue below */ + /* Parse the CBOR structure - read subtrees */ + r = lydcbor_subtree_r(lydctx, parent, first_p, parsed, lydctx->cborctx->cbor_data); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - /* Parse the CBOR structure */ - ret = lydcbor_subtree_r(lydctx, parent, first_p, parsed, cbor_data); + /* Unexpected sibling node error handling */ -cleanup: - if (cbor_data) - { - cbor_decref(&cbor_data); + /* Validate operation node presence */ + if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && + !lydctx->op_node) { + LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); + r = LY_EVALID; + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } + /* also need to deal with metadata linking etc*/ - if (ret) - { - if (lydctx) - { - lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); - lydctx = NULL; - } +cleanup: + if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { + lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); + lydctx = NULL; } *lydctx_p = (struct lyd_ctx *)lydctx; - return ret; + return rc; } #endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file diff --git a/src/parser_data.h b/src/parser_data.h index accfff976..a423c2892 100644 --- a/src/parser_data.h +++ b/src/parser_data.h @@ -44,7 +44,8 @@ struct ly_in; * - CBOR * * The reference documentation would be `Encoding of Data Modeled with YANG in the Concise Binary Object - * Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254) + * Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254). < $TODO$ Look at the edge cases of + * RPCs, actions and Notifications and maybe like json only - where we make a proprietary representation> * * While the parsers themselves process the input data only syntactically, all the parser functions actually incorporate * the [common validator](@ref howtoDataValidation) checking the input data semantically. Therefore, the parser functions diff --git a/src/parser_internal.h b/src/parser_internal.h index 4ce7ba68e..590fd74e0 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -200,6 +200,7 @@ struct lyd_cbor_ctx lyd_ctx_free_clb free; /**< destructor */ struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ + const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */ }; #endif /* ENABLE_CBOR_SUPPORT */ From d0f09493af70c644b4568750f152c8c8c37aa271 Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Mon, 20 Oct 2025 06:46:18 +0100 Subject: [PATCH 5/8] feat: complete CBOR parser with metadata, opaque nodes, and LY_VALUE_CBOR --- src/lcbor.c | 30 - src/parser_cbor.c | 2186 +++++++++++++++++++-------- src/parser_json.c | 1 + src/plugins_types.c | 2 + src/plugins_types/instanceid.c | 1 + src/plugins_types/instanceid_keys.c | 1 + src/plugins_types/node_instanceid.c | 2 + src/plugins_types/xpath1.0.c | 1 + src/printer_json.c | 2 + src/printer_lyb.c | 1 + src/printer_xml.c | 1 + src/schema_compile_node.c | 1 + src/tree.h | 2 +- src/tree_data_common.c | 8 + src/tree_data_new.c | 1 + src/tree_schema_internal.h | 2 + src/xpath.c | 2 + 17 files changed, 1549 insertions(+), 695 deletions(-) diff --git a/src/lcbor.c b/src/lcbor.c index e92452eec..097ea7eec 100644 --- a/src/lcbor.c +++ b/src/lcbor.c @@ -45,36 +45,6 @@ lycbor_token2str(enum cbor_type cbortype) return "decimals and special values (true, false, nil, ...)"; } - return ""; -} - - return "object"; - case LYJSON_OBJECT_NEXT: - return "object next"; - case LYJSON_OBJECT_CLOSED: - return "object closed"; - case LYJSON_ARRAY: - return "array"; - case LYJSON_ARRAY_NEXT: - return "array next"; - case LYJSON_ARRAY_CLOSED: - return "array closed"; - case LYJSON_OBJECT_NAME: - return "object name"; - case LYJSON_NUMBER: - return "number"; - case LYJSON_STRING: - return "string"; - case LYJSON_TRUE: - return "true"; - case LYJSON_FALSE: - return "false"; - case LYJSON_NULL: - return "null"; - case LYJSON_END: - return "end of input"; - } - return ""; } diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 6b7613848..064dbe62d 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -38,7 +38,6 @@ #include "tree_schema.h" #include "validation.h" - #include #include void print_json(cbor_item_t *item); @@ -86,16 +85,26 @@ void print_json_map(const cbor_item_t *item) void print_json_number(const cbor_item_t *item) { - if (cbor_isa_uint(item)) { + if (cbor_isa_uint(item)) + { printf("%lu", cbor_get_uint64(item)); - } else if (cbor_isa_negint(item)) { + } + else if (cbor_isa_negint(item)) + { printf("-%lu", cbor_get_uint64(item) + 1); - } else if (cbor_isa_float_ctrl(item)) { - if (cbor_float_get_width(item) == CBOR_FLOAT_64) { + } + else if (cbor_isa_float_ctrl(item)) + { + if (cbor_float_get_width(item) == CBOR_FLOAT_64) + { printf("%f", cbor_float_get_float8(item)); - } else if (cbor_float_get_width(item) == CBOR_FLOAT_32) { + } + else if (cbor_float_get_width(item) == CBOR_FLOAT_32) + { printf("%f", cbor_float_get_float4(item)); - } else if (cbor_float_get_width(item) == CBOR_FLOAT_16) { + } + else if (cbor_float_get_width(item) == CBOR_FLOAT_16) + { printf("%f", cbor_float_get_float2(item)); } } @@ -113,7 +122,8 @@ void print_json_null(const cbor_item_t *item) void print_json(cbor_item_t *item) { - if (!item) { + if (!item) + { printf("null"); return; } @@ -137,20 +147,32 @@ void print_json(cbor_item_t *item) else if (cbor_isa_float_ctrl(item)) { // Check if it's a control value (null, undefined, true, false) - if (cbor_float_get_width(item) == CBOR_FLOAT_0) { + if (cbor_float_get_width(item) == CBOR_FLOAT_0) + { uint8_t ctrl = cbor_ctrl_value(item); - if (ctrl == 20) { + if (ctrl == 20) + { printf("false"); - } else if (ctrl == 21) { + } + else if (ctrl == 21) + { printf("true"); - } else if (ctrl == 22) { + } + else if (ctrl == 22) + { printf("null"); - } else if (ctrl == 23) { + } + else if (ctrl == 23) + { printf("undefined"); - } else { + } + else + { printf("null"); // unknown control value } - } else { + } + else + { print_json_number(item); } } @@ -164,14 +186,10 @@ void print_json(cbor_item_t *item) } } -static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); - /** - * @brief Free the CBOR data parser context - * CBOR implementation of lyd_ctx_free_clb(). + * @brief Free the CBOR data parser context. * - * @param[in] lydctx Data parser context to free. + * CBOR implementation of lyd_ctx_free_clb(). */ static void lyd_cbor_ctx_free(struct lyd_ctx *lydctx) @@ -187,10 +205,367 @@ lyd_cbor_ctx_free(struct lyd_ctx *lydctx) } /** - * @brief Convert a CBOR item to a string representation. + * @brief Parse CBOR member-name as [\@][prefix:][name] + * + * \@ - metadata flag, maps to 1 in @p is_meta_p + * prefix - name of the module of the data node + * name - name of the data node + * + * All the output parameters are mandatory. Function only parses the member-name. + * + * @param[in] value String to parse + * @param[in] value_len Length of the @p value. + * @param[out] name_p Pointer to the beginning of the parsed name. + * @param[out] name_len_p Pointer to the length of the parsed name. + * @param[out] prefix_p Pointer to the beginning of the parsed prefix. If the member-name does not contain prefix, result is NULL. + * @param[out] prefix_len_p Pointer to the length of the parsed prefix. If the member-name does not contain prefix, result is 0. + * @param[out] is_meta_p Pointer to the metadata flag, set to 1 if the member-name contains \@, 0 otherwise. + */ +static void +lydcbor_parse_name(const char *value, size_t value_len, const char **name_p, size_t *name_len_p, const char **prefix_p, + size_t *prefix_len_p, ly_bool *is_meta_p) +{ + const char *name, *prefix = NULL; + size_t name_len, prefix_len = 0; + ly_bool is_meta = 0; + + name = memchr(value, ':', value_len); + if (name != NULL) + { + prefix = value; + if (*prefix == '@') + { + is_meta = 1; + prefix++; + } + prefix_len = name - prefix; + name++; + name_len = value_len - (prefix_len + 1) - is_meta; + } + else + { + name = value; + if (name[0] == '@') + { + is_meta = 1; + name++; + } + name_len = value_len - is_meta; + } + + *name_p = name; + *name_len_p = name_len; + *prefix_p = prefix; + *prefix_len_p = prefix_len; + *is_meta_p = is_meta; +} + +/** + * @brief Get correct prefix (module_name) inside the @p node. + * + * @param[in] node Data node to get inherited prefix. + * @param[in] local_prefix Local prefix to replace the inherited prefix. + * @param[in] local_prefix_len Length of the @p local_prefix string. In case of 0, the inherited prefix is taken. + * @param[out] prefix_p Pointer to the resulting prefix string. + * @param[out] prefix_len_p Pointer to the length of the resulting @p prefix_p string. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t local_prefix_len, const char **prefix_p, + size_t *prefix_len_p) +{ + struct lyd_node_opaq *onode; + const char *module_name = NULL; + + assert(prefix_p && prefix_len_p); + + if (local_prefix_len) + { + *prefix_p = local_prefix; + *prefix_len_p = local_prefix_len; + return LY_SUCCESS; + } + + while (node) + { + if (node->schema) + { + module_name = node->schema->module->name; + break; + } + onode = (struct lyd_node_opaq *)node; + if (onode->name.module_name) + { + module_name = onode->name.module_name; + break; + } + else if (onode->name.prefix) + { + module_name = onode->name.prefix; + break; + } + node = lyd_parent(node); + } + + *prefix_p = module_name; + *prefix_len_p = ly_strlen(module_name); + return LY_SUCCESS; +} + +/** + * @brief Skip the current CBOR item based on its type. + * + * @param[in] cborctx CBOR context. + * @param[in] item CBOR item to skip. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_data_skip(struct lycbor_ctx *cborctx) +{ + (void)cborctx; + /* In CBOR, items are already parsed, so skipping is implicit */ + return LY_SUCCESS; +} + +/** + * @brief Get schema node corresponding to the input parameters. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] is_attr Flag if the reference to the node is an attribute. + * @param[in] prefix Requested node's prefix (module name). + * @param[in] prefix_len Length of the @p prefix. + * @param[in] name Requested node's name. + * @param[in] name_len Length of the @p name. + * @param[in] parent Parent of the node being processed. + * @param[out] snode Found schema node corresponding to the input parameters. + * @param[out] ext Extension instance that provided @p snode, if any. + * @return LY_SUCCESS on success. + * @return LY_ENOT if the whole object was parsed (skipped or as an extension). + * @return LY_ERR on error. + */ +static LY_ERR +lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name, + size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) +{ + LY_ERR ret = LY_SUCCESS, r; + struct lys_module *mod = NULL; + uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; + + *snode = NULL; + *ext = NULL; + + /* get the element module, prefer parent context because of extensions */ + if (prefix_len) + { + mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->cborctx->ctx, prefix, prefix_len); + } + else if (parent) + { + if (parent->schema) + { + mod = parent->schema->module; + } + } + else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Top-level CBOR object member \"%.*s\" must be namespace-qualified.", + (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name); + ret = LY_EVALID; + goto cleanup; + } + if (!mod) + { + /* check for extension data */ + r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_CBOR, NULL, name, name_len, snode, ext); + if (r != LY_ENOT) + { + ret = r; + goto cleanup; + } + + /* unknown module */ + if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix); + ret = LY_EVALID; + goto cleanup; + } + } + + /* get the schema node */ + if (mod && (!parent || parent->schema)) + { + if (!parent && lydctx->ext) + { + *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts); + } + else + { + *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts); + } + if (!*snode) + { + /* check for extension data */ + r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_CBOR, NULL, name, name_len, snode, ext); + if (r != LY_ENOT) + { + ret = r; + goto cleanup; + } + + /* unknown data node */ + printf("checkpoint1-web\n"); + if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + if (parent) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", + (int)name_len, name, LYD_NAME(parent)); + } + else if (lydctx->ext) + { + if (lydctx->ext->argument) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, + "Node \"%.*s\" not found in the \"%s\" %s extension instance.", + (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name); + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.", + (int)name_len, name, lydctx->ext->def->name); + } + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", + (int)name_len, name, mod->name); + } + ret = LY_EVALID; + goto cleanup; + } + } + else + { + /* check that schema node is valid and can be used */ + ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode); + } + } + +cleanup: + return ret; +} + +/** + * @brief Check if CBOR item is null/undefined. + * + * @param[in] item CBOR item to check. + * @return 1 if null/undefined, 0 otherwise. + */ +static ly_bool +lydcbor_is_null(const cbor_item_t *item) +{ + if (!item) + { + return 1; + } + + if (cbor_isa_float_ctrl(item)) + { + if (cbor_float_get_width(item) == CBOR_FLOAT_0) + { + uint8_t ctrl = cbor_ctrl_value(item); + if (ctrl == 22 || ctrl == 23) + { /* null or undefined */ + return 1; + } + } + } + + return 0; +} + +/** + * @brief Get the hint for the data type parsers according to the current CBOR type. * - * This function handles the low-level CBOR to string conversion, - * similar to how JSON parser converts JSON values to strings. + * @param[in] lydctx CBOR data parser context. + * @param[in] item CBOR item. + * @param[out] type_hint_p Pointer to the variable to store the result. + * @return LY_SUCCESS in case of success. + * @return LY_EINVAL in case of invalid CBOR type. + */ +static LY_ERR +lydcbor_value_type_hint(struct lyd_cbor_ctx *lydctx, const cbor_item_t *item, uint32_t *type_hint_p) +{ + enum cbor_type type; + + *type_hint_p = 0; + + if (!item) + { + return LY_EINVAL; + } + + type = cbor_typeof(item); + + if (type == CBOR_TYPE_ARRAY) + { + /* check for [null] */ + if (cbor_array_size(item) == 1) + { + cbor_item_t **handle = cbor_array_handle(item); + if (handle && lydcbor_is_null(handle[0])) + { + *type_hint_p = LYD_VALHINT_EMPTY; + return LY_SUCCESS; + } + } + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR value or [null], but array found."); + return LY_EINVAL; + } + else if (type == CBOR_TYPE_STRING) + { + *type_hint_p = LYD_VALHINT_STRING | LYD_VALHINT_NUM64; + } + else if (type == CBOR_TYPE_UINT || type == CBOR_TYPE_NEGINT) + { + *type_hint_p = LYD_VALHINT_DECNUM; + } + else if (type == CBOR_TYPE_FLOAT_CTRL) + { + if (cbor_float_ctrl_is_ctrl(item)) + { + uint8_t ctrl = cbor_ctrl_value(item); + if (ctrl == CBOR_CTRL_TRUE || ctrl == CBOR_CTRL_FALSE) + { + *type_hint_p = LYD_VALHINT_BOOLEAN; + } + else if (ctrl == CBOR_CTRL_NULL) + { + *type_hint_p = 0; + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unexpected CBOR control value."); + return LY_EINVAL; + } + } + else + { + *type_hint_p = LYD_VALHINT_DECNUM; + } + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unexpected CBOR data type."); + return LY_EINVAL; + } + + return LY_SUCCESS; +} + +/** + * @brief Convert a CBOR item to a string representation. * * @param[in] item CBOR item to convert. * @param[out] str_val String value (allocated, caller must free). @@ -206,14 +581,11 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) *str_val = NULL; *str_len = 0; - switch (cbor_typeof(item)) - { - case CBOR_TYPE_UINT: - { + switch (cbor_typeof(item)) { + case CBOR_TYPE_UINT: { uint64_t val = cbor_get_int(item); int len = snprintf(NULL, 0, "%" PRIu64, val); - if (len < 0) - { + if (len < 0) { return LY_ESYS; } *str_val = malloc(len + 1); @@ -222,12 +594,10 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) *str_len = len; break; } - case CBOR_TYPE_NEGINT: - { + case CBOR_TYPE_NEGINT: { int64_t val = -1 - (int64_t)cbor_get_int(item); int len = snprintf(NULL, 0, "%" PRId64, val); - if (len < 0) - { + if (len < 0) { return LY_ESYS; } *str_val = malloc(len + 1); @@ -251,10 +621,8 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) (*str_val)[*str_len] = '\0'; break; case CBOR_TYPE_FLOAT_CTRL: - if (cbor_float_ctrl_is_ctrl(item)) - { - switch (cbor_ctrl_value(item)) - { + if (cbor_float_ctrl_is_ctrl(item)) { + switch (cbor_ctrl_value(item)) { case CBOR_CTRL_TRUE: *str_val = strdup("true"); *str_len = 4; @@ -273,14 +641,10 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) break; } LY_CHECK_ERR_RET(!*str_val, LOGMEM(NULL), LY_EMEM); - } - else - { - /* Float value */ + } else { double val = cbor_float_get_float(item); int len = snprintf(NULL, 0, "%g", val); - if (len < 0) - { + if (len < 0) { return LY_ESYS; } *str_val = malloc(len + 1); @@ -299,855 +663,1327 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) } /** - * @brief Get string key from CBOR map item. + * @brief Check in advance if the input data are parsable according to the provided @p snode. + * + * Note that the checks are done only in case the LYD_PARSE_OPAQ is allowed. Otherwise the same checking + * is naturally done when the data are really parsed. * - * For named identifier format, keys should be strings. - * For SID format, keys would be integers (future implementation). + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node corresponding to the member currently being processed. + * @param[in] cbor_value CBOR value to check. + * @param[out] type_hint_p Pointer to store detected value type hint. + * @return LY_SUCCESS if data are parsable. + * @return LY_ENOT if input data are not sufficient. + * @return LY_EINVAL in case of invalid encoding. */ static LY_ERR -lydcbor_get_key_string(struct lyd_cbor_ctx *lydctx, const cbor_item_t *key_item, - char **key_str, size_t *key_len) +lydcbor_data_check_opaq(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, const cbor_item_t *cbor_value, + uint32_t *type_hint_p) { LY_ERR ret = LY_SUCCESS; + uint32_t *prev_lo, temp_lo = 0; + char *str_val = NULL; + size_t str_len = 0; - assert(lydctx && key_item && key_str && key_len); + assert(snode); - switch (lydctx->cborctx->format) + if (!(snode->nodetype & (LYD_NODE_TERM | LYS_LIST))) { - case LYD_CBOR_NAMED: - /* Keys must be strings for named format */ - if (!cbor_isa_string(key_item)) + return LY_SUCCESS; + } + + if (lydctx->parse_opts & LYD_PARSE_OPAQ) + { + switch (snode->nodetype) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); - return LY_EVALID; + case LYS_LEAFLIST: + case LYS_LEAF: + if ((ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p))) + { + break; + } + + prev_lo = ly_temp_log_options(&temp_lo); + ret = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + if (ret == LY_SUCCESS) + { + if (ly_value_validate(NULL, snode, str_val, str_len, LY_VALUE_CBOR, NULL, *type_hint_p)) + { + ret = LY_ENOT; + } + } + ly_temp_log_options(prev_lo); + free(str_val); + break; + case LYS_LIST: + /* Lists may not have all keys - handled elsewhere */ + break; } - ret = lydcbor_item_to_string(key_item, key_str, key_len); - break; - case LYD_CBOR_SID: - /* Future: Handle SID integer keys */ - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "CBOR SID format not yet implemented"); - ret = LY_ENOT; - break; - default: - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unknown CBOR format"); - ret = LY_EINVAL; - break; + } + else if (snode->nodetype & LYD_NODE_TERM) + { + ret = lydcbor_value_type_hint(lydctx, cbor_value, type_hint_p); } return ret; } /** - * @brief Parse a single CBOR value according to schema node. + * @brief Join the forward-referencing metadata with their target data nodes. * - * @param[in] lydctx CBOR parser context. - * @param[in] snode Schema node for the value. - * @param[in] cbor_item CBOR item to parse. - * @param[out] node Created data node. - * @return LY_ERR value. + * @param[in] lydctx CBOR data parser context. + * @param[in,out] first_p Pointer to the first sibling node. + * @return LY_SUCCESS on success. + * @return LY_EVALID if there are unresolved metadata. */ static LY_ERR -lydcbor_parse_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const void *cbor_item, struct lyd_node **node) +lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) { LY_ERR ret = LY_SUCCESS; - const cbor_item_t *item = (const cbor_item_t *)cbor_item; - char *str_val = NULL; - size_t str_len = 0; - - assert(lydctx && snode && item && node); + struct lyd_node *node, *attr, *next, *meta_iter; + struct lysc_ext_instance *ext; + uint64_t instance = 0; + const char *prev = NULL; + uint32_t log_location_items = 0; + + /* finish linking metadata */ + LY_LIST_FOR_SAFE(*first_p, next, attr) + { + struct lyd_node_opaq *meta_container = (struct lyd_node_opaq *)attr; + uint64_t match = 0; + ly_bool is_attr; + const char *name, *prefix; + size_t name_len, prefix_len; + const struct lysc_node *snode; + + if (attr->schema || (meta_container->name.name[0] != '@')) + { + continue; + } - /* Convert CBOR value to string */ - LY_CHECK_GOTO(ret = lydcbor_item_to_string(item, &str_val, &str_len), cleanup); + LOG_LOCSET(NULL, attr); + log_location_items++; - /* Create data node based on schema node type */ - switch (snode->nodetype) - { - case LYS_LEAF: - case LYS_LEAFLIST: - ret = lyd_create_term(snode, str_val, str_len, 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, node); - break; - case LYS_ANYDATA: - case LYS_ANYXML: - /* For anydata/anyxml, we store the CBOR directly */ - ret = lyd_create_any(snode, cbor_item, LYD_ANYDATA_CBOR, 0, node); - break; - default: - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type for CBOR value"); - ret = LY_EVALID; - break; + if (prev != meta_container->name.name) + { + lydict_remove(lydctx->cborctx->ctx, prev); + LY_CHECK_GOTO(ret = lydict_insert(lydctx->cborctx->ctx, meta_container->name.name, 0, &prev), cleanup); + instance = 1; + } + else + { + instance++; + } + + /* find the corresponding data node */ + LY_LIST_FOR(*first_p, node) + { + if (!node->schema) + { + /* opaq node */ + if (strcmp(&meta_container->name.name[1], ((struct lyd_node_opaq *)node)->name.name)) + { + continue; + } + + if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", + ((struct lyd_node_opaq *)node)->name.name); + ret = LY_EVALID; + goto cleanup; + } + + match++; + if (match != instance) + { + continue; + } + + LY_LIST_FOR(meta_container->child, meta_iter) + { + struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter; + + ret = lyd_create_attr(node, NULL, lydctx->cborctx->ctx, meta->name.name, strlen(meta->name.name), + meta->name.prefix, ly_strlen(meta->name.prefix), meta->name.module_name, + ly_strlen(meta->name.module_name), meta->value, ly_strlen(meta->value), NULL, LY_VALUE_CBOR, + NULL, meta->hints); + LY_CHECK_GOTO(ret, cleanup); + } + break; + } + else + { + lydcbor_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len, + &prefix, &prefix_len, &is_attr); + assert(is_attr); + lydcbor_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext); + + if (snode != node->schema) + { + continue; + } + + match++; + if (match != instance) + { + continue; + } + + LY_LIST_FOR(meta_container->child, meta_iter) + { + struct lyd_node_opaq *meta = (struct lyd_node_opaq *)meta_iter; + struct lys_module *mod = NULL; + + mod = ly_ctx_get_module_implemented(lydctx->cborctx->ctx, meta->name.prefix); + if (mod) + { + ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, + meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value), + NULL, LY_VALUE_CBOR, NULL, meta->hints, node->schema); + LY_CHECK_GOTO(ret, cleanup); + } + else if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + if (meta->name.prefix) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, + "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", + meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", + meta->name.name); + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", + meta->name.name); + } + ret = LY_EVALID; + goto cleanup; + } + } + + ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext); + LY_CHECK_GOTO(ret, cleanup); + break; + } + } + + if (match != instance) + { + if (instance > 1) + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, + "Missing CBOR data instance #%" PRIu64 " to be coupled with %s metadata.", + instance, meta_container->name.name); + } + else + { + LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Missing CBOR data instance to be coupled with %s metadata.", + meta_container->name.name); + } + ret = LY_EVALID; + } + else + { + if (attr == (*first_p)) + { + *first_p = attr->next; + } + lyd_free_tree(attr); + } + + LOG_LOCBACK(0, log_location_items); + log_location_items = 0; } cleanup: - free(str_val); + lydict_remove(lydctx->cborctx->ctx, prev); + LOG_LOCBACK(0, log_location_items); return ret; } +/** + * @brief Parse a metadata member/attribute from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node of the metadata parent. + * @param[in] node Parent node. + * @param[in] cbor_meta CBOR metadata item. + * @return LY_ERR value. + */ static LY_ERR -lydcbor_parse_node_value(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - struct lyd_node **node, const cbor_item_t *cbor_value) +lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node, + const cbor_item_t *cbor_meta) { - LY_ERR ret = LY_SUCCESS; + LY_ERR rc = LY_SUCCESS, r; + enum cbor_type type; + const char *expected; + ly_bool in_parent = 0; + const char *name, *prefix = NULL; + size_t name_len, prefix_len = 0; + struct lys_module *mod; + const struct ly_ctx *ctx = lydctx->cborctx->ctx; + ly_bool is_attr = 0; + struct lyd_node *prev = node; + uint32_t instance = 0, val_hints; + uint16_t nodetype; + struct cbor_pair *pairs; + size_t map_size; + + assert(snode || node); + + nodetype = snode ? snode->nodetype : LYS_CONTAINER; + if (snode) + { + LOG_LOCSET(snode, NULL); + } - assert(lydctx && snode && node && cbor_value); - *node = NULL; + type = cbor_typeof(cbor_meta); - switch (snode->nodetype) + /* check attribute encoding */ + switch (nodetype) { - case LYS_CONTAINER: - ret = lyd_create_inner(snode, node); - break; - case LYS_LIST: - ret = lyd_create_inner(snode, node); - break; - case LYS_LEAF: case LYS_LEAFLIST: - { - char *str_val = NULL; - size_t str_len = 0; + expected = "@name/array of objects/nulls"; + LY_CHECK_GOTO(type != CBOR_TYPE_ARRAY, representation_error); + + next_entry: + instance++; + if (!node || (node->schema != prev->schema)) + { + LOGVAL(ctx, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); + rc = LY_EVALID; + goto cleanup; + } + + /* Process array item */ + if (cbor_array_size(cbor_meta) > instance - 1) + { + cbor_item_t **handle = cbor_array_handle(cbor_meta); + const cbor_item_t *item = handle[instance - 1]; - ret = lydcbor_item_to_string(cbor_value, &str_val, &str_len); - if (ret == LY_SUCCESS) + if (lydcbor_is_null(item)) + { + prev = node; + node = node->next; + if (instance < cbor_array_size(cbor_meta)) + { + goto next_entry; + } + goto cleanup; + } + } + else { - ret = lyd_create_term(snode, str_val, str_len, 0, 0, NULL, LY_VALUE_JSON, NULL, LYD_HINT_DATA, NULL, node); + goto cleanup; } - free(str_val); break; - } - case LYS_ANYDATA: + case LYS_LEAF: case LYS_ANYXML: - ret = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, node); + expected = "@name/object"; + LY_CHECK_GOTO(type != CBOR_TYPE_MAP, representation_error); break; - default: - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type %d", snode->nodetype); - ret = LY_EVALID; + case LYS_CONTAINER: + case LYS_LIST: + case LYS_ANYDATA: + case LYS_NOTIF: + case LYS_ACTION: + case LYS_RPC: + in_parent = 1; + expected = "@/object"; + LY_CHECK_GOTO(type != CBOR_TYPE_MAP, representation_error); break; + default: + LOGINT(ctx); + rc = LY_EINT; + goto cleanup; } - return ret; -} + /* process all members inside metadata object */ + assert(type == CBOR_TYPE_MAP); + map_size = cbor_map_size(cbor_meta); + pairs = cbor_map_handle(cbor_meta); -static LY_ERR -lydcbor_parse_terminal(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) -{ - LY_ERR ret; - struct lyd_node *node = NULL; + for (size_t i = 0; i < map_size; ++i) + { + const cbor_item_t *key_item = pairs[i].key; + const cbor_item_t *value_item = pairs[i].value; + char *key_str = NULL; + size_t key_len = 0; - ret = lydcbor_parse_node_value(lydctx, snode, &node, cbor_value); - LY_CHECK_RET(ret); + if (!cbor_isa_string(key_item)) + { + LOGVAL(ctx, LYVE_SYNTAX, "Metadata key must be a string."); + rc = LY_EVALID; + goto cleanup; + } - if (!node) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create terminal node for \"%s\"", snode->name); - return LY_EVALID; - } + LY_CHECK_GOTO(rc = lydcbor_item_to_string(key_item, &key_str, &key_len), cleanup); - lyd_hash(node); - /* Insert into tree */ - ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) - { - lyd_free_tree(node); - return ret; - } + lydcbor_parse_name(key_str, key_len, &name, &name_len, &prefix, &prefix_len, &is_attr); - /* Add to parsed set */ - return ly_set_add(parsed, node, 1, NULL); -} + if (!name_len) + { + LOGVAL(ctx, LYVE_SYNTAX, "Metadata in CBOR found with an empty name."); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + else if (!prefix_len) + { + LOGVAL(ctx, LYVE_SYNTAX, "Metadata in CBOR must be namespace-qualified, missing prefix for \"%.*s\".", + (int)key_len, key_str); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + else if (is_attr) + { + LOGVAL(ctx, LYVE_SYNTAX, "Invalid format of Metadata identifier in CBOR, unexpected '@' in \"%.*s\"", + (int)key_len, key_str); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } -static LY_ERR -lydcbor_parse_container(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) -{ - LY_ERR ret; - struct lyd_node *node = NULL; + /* get the element module */ + mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len); + if (!mod) + { + if (lydctx->parse_opts & LYD_PARSE_STRICT) + { + LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", + (int)prefix_len, prefix, (int)name_len, name); + free(key_str); + rc = LY_EVALID; + goto cleanup; + } + if (node->schema) + { + free(key_str); + continue; + } + assert(lydctx->parse_opts & LYD_PARSE_OPAQ); + } - ret = lyd_create_inner(snode, &node); - LY_CHECK_RET(ret); + /* get value hints */ + LY_CHECK_ERR_GOTO(rc = lydcbor_value_type_hint(lydctx, value_item, &val_hints), free(key_str), cleanup); - if (!node) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create container node for \"%s\"", snode->name); - return LY_EVALID; + if (node->schema) + { + char *str_val = NULL; + size_t str_len = 0; + + LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(value_item, &str_val, &str_len), free(key_str), cleanup); + + /* create metadata */ + rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, str_val, + str_len, NULL, LY_VALUE_CBOR, NULL, val_hints, node->schema); + free(str_val); + LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); + + /* add/correct flags */ + rc = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL); + LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); + } + else + { + /* create attribute */ + const char *module_name; + size_t module_name_len; + char *str_val = NULL; + size_t str_len = 0; + + lydcbor_get_node_prefix(node, prefix, prefix_len, &module_name, &module_name_len); + + LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(value_item, &str_val, &str_len), free(key_str), cleanup); + + rc = lyd_create_attr(node, NULL, ctx, name, name_len, prefix, prefix_len, module_name, + module_name_len, str_val, str_len, NULL, LY_VALUE_CBOR, NULL, val_hints); + free(str_val); + LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); + } + + free(key_str); } - lyd_hash(node); - /* Insert into tree first */ - ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) + if (nodetype == LYS_LEAFLIST && instance < cbor_array_size(cbor_meta)) { - lyd_free_tree(node); - return ret; + prev = node; + node = node->next; + goto next_entry; } - /* Add to parsed set */ - ret = ly_set_add(parsed, node, 1, NULL); - LY_CHECK_RET(ret); + goto cleanup; - /* Parse container children */ - if (cbor_isa_map(cbor_value) && cbor_map_size(cbor_value) > 0) - { - struct lyd_node *child_first = NULL; - ret = lydcbor_subtree_r(lydctx, node, &child_first, parsed, cbor_value); - if (ret) - { - return ret; - } +representation_error: + LOGVAL(ctx, LYVE_SYNTAX, + "The attribute(s) of %s \"%s\" is expected to be represented as CBOR %s, but input data contains different type.", + lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected); + rc = LY_EVALID; - /* Link children to container */ - if (child_first) +cleanup: + if ((rc == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) + { + if ((r = lydcbor_data_skip(lydctx->cborctx))) { - lyd_insert_child(node, child_first); + rc = r; } } - - return LY_SUCCESS; + LOG_LOCBACK(snode ? 1 : 0, 0); + return rc; } -/* Helper function to check if CBOR item is null/undefined */ -static ly_bool -lydcbor_is_null(const cbor_item_t *item) +/** + * @brief Maintain children - insert node and update first pointer. + * + * @param[in] parent Parent node to insert to. + * @param[in,out] first_p Pointer to the first sibling. + * @param[in,out] node_p Pointer to the node to insert. + * @param[in] last If set, insert at the end. + * @param[in] ext Extension instance. + */ +static void +lydcbor_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last, + struct lysc_ext_instance *ext) { - if (!item) { - return 1; + if (!*node_p) + { + return; } - - /* Check for CBOR null primitive */ - if (cbor_isa_float_ctrl(item)) { - if (cbor_float_get_width(item) == CBOR_FLOAT_0) { - uint8_t ctrl = cbor_ctrl_value(item); - if (ctrl == 22 || ctrl == 23) { /* null or undefined */ - return 1; + + if (ext) + { + lyplg_ext_insert(parent, *node_p); + } + else + { + lyd_insert_node(parent, first_p, *node_p, last); + } + if (first_p) + { + if (parent) + { + *first_p = lyd_child(parent); + } + else + { + while ((*first_p)->prev->next) + { + *first_p = (*first_p)->prev; } } } - - return 0; + *node_p = NULL; } +/** + * @brief Create an opaq node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] name Name of the opaq node. + * @param[in] name_len Length of @p name. + * @param[in] prefix Prefix of the opaq node. + * @param[in] prefix_len Length of @p prefix. + * @param[in] parent Data parent. + * @param[in] cbor_value CBOR value item. + * @param[out] node_p Pointer to the created opaq node. + * @return LY_ERR value. + */ static LY_ERR -lydcbor_parse_list_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) +lydcbor_create_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, + struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **node_p) { LY_ERR ret = LY_SUCCESS; - struct lyd_node *node = NULL; - size_t array_size; - cbor_item_t **array_handle; - - assert(lydctx && snode && array_item && parsed); + const char *value = NULL, *module_name; + size_t value_len = 0, module_name_len = 0; + ly_bool dynamic = 0; + uint32_t type_hint = 0; + char *str_val = NULL; - if (!cbor_isa_array(array_item)) + if (cbor_typeof(cbor_value) != CBOR_TYPE_MAP) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for list"); - return LY_EVALID; + /* prepare for creating opaq node with a value */ + LY_CHECK_RET(lydcbor_item_to_string(cbor_value, &str_val, &value_len)); + value = str_val; + dynamic = 1; + + LY_CHECK_GOTO(ret = lydcbor_value_type_hint(lydctx, cbor_value, &type_hint), cleanup); } - if (snode->nodetype != LYS_LIST) + /* get the module name */ + lydcbor_get_node_prefix(parent, prefix, prefix_len, &module_name, &module_name_len); + if (!module_name && !parent && lydctx->any_schema) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be list"); - return LY_EVALID; + module_name = lydctx->any_schema->module->name; + module_name_len = strlen(module_name); } - array_size = cbor_array_size(array_item); - array_handle = cbor_array_handle(array_item); + /* create node */ + ret = lyd_create_opaq(lydctx->cborctx->ctx, name, name_len, prefix, prefix_len, module_name, module_name_len, value, + value_len, &dynamic, LY_VALUE_CBOR, NULL, type_hint, node_p); - if (!array_handle && array_size > 0) +cleanup: + if (dynamic) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); - return LY_EVALID; + free((char *)value); } + return ret; +} - for (size_t i = 0; i < array_size; ++i) - { - const cbor_item_t *item = array_handle[i]; +static LY_ERR lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, + struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj); - if (!item) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); - ret = LY_EVALID; - goto cleanup; - } +/** + * @brief Parse opaq node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] name Name of the opaq node. + * @param[in] name_len Length of @p name. + * @param[in] prefix Prefix of the opaq node. + * @param[in] prefix_len Length of @p prefix. + * @param[in] parent Data parent. + * @param[in] cbor_value CBOR value item. + * @param[in,out] first_p First top-level/parent sibling. + * @param[out] node_p Pointer to the created opaq node. + * @return LY_ERR value. + */ +static LY_ERR +lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, + struct lyd_node *parent, const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) +{ + LY_ERR ret = LY_SUCCESS; + enum cbor_type type = cbor_typeof(cbor_value); + + LY_CHECK_GOTO(ret = lydcbor_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, cbor_value, node_p), cleanup); + + assert(*node_p); + LOG_LOCSET(NULL, *node_p); - if (!cbor_isa_map(item)) + if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) + { + cbor_item_t **handle = cbor_array_handle(cbor_value); + if (lydcbor_is_null(handle[0])) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "List entry must be a CBOR map"); - ret = LY_EVALID; - goto cleanup; + /* special array null value */ + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY; + goto finish; } + } - ret = lyd_create_inner(snode, &node); - LY_CHECK_GOTO(ret, cleanup); + if (type == CBOR_TYPE_ARRAY) + { + /* process array */ + size_t array_size = cbor_array_size(cbor_value); + cbor_item_t **array_handle = cbor_array_handle(cbor_value); - if (!node) + for (size_t i = 0; i < array_size; ++i) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create list node"); - ret = LY_EVALID; - goto cleanup; - } - lyd_hash(node); - /* Insert the list node */ - ret = lyd_insert_sibling(*first_p, node, first_p); - LY_CHECK_GOTO(ret, cleanup); + const cbor_item_t *item = array_handle[i]; - /* Add to parsed set */ - LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); + if (cbor_typeof(item) == CBOR_TYPE_MAP) + { + /* array with objects, list */ + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST; - /* Parse list entry content */ - struct lyd_node *child_first = NULL; - ret = lydcbor_subtree_r(lydctx, node, &child_first, parsed, item); - LY_CHECK_GOTO(ret, cleanup); + /* process children */ + LY_CHECK_GOTO(ret = lydcbor_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL, item), cleanup); + } + else + { + /* array with values, leaf-list */ + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST; + } - /* Link children to list entry */ - if (child_first) - { - lyd_insert_child(node, child_first); - } + if (i < array_size - 1) + { + /* continue with next instance */ + assert(*node_p); + lydcbor_maintain_children(parent, first_p, node_p, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); + + LOG_LOCBACK(0, 1); + + LY_CHECK_GOTO(ret = lydcbor_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, item, node_p), cleanup); - node = NULL; /* Reset pointer after successful processing */ + assert(*node_p); + LOG_LOCSET(NULL, *node_p); + } + } + } + else if (type == CBOR_TYPE_MAP) + { + ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_CONTAINER; + /* process children */ + LY_CHECK_GOTO(ret = lydcbor_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL, cbor_value), cleanup); } +finish: + /* finish linking metadata */ + ret = lydcbor_metadata_finish(lydctx, lyd_node_child_p(*node_p)); + cleanup: - if (ret && node) + if (*node_p) { - lyd_free_tree(node); + LOG_LOCBACK(0, 1); } return ret; } +/** + * @brief Process the attribute container (starting by @). + * + * @param[in] lydctx CBOR data parser context. + * @param[in] attr_node The data node referenced by the attribute container. + * @param[in] snode The schema node of the data node. + * @param[in] name Name of the node. + * @param[in] name_len Length of @p name. + * @param[in] prefix Prefix of the node. + * @param[in] prefix_len Length of @p prefix. + * @param[in] parent Data parent. + * @param[in] cbor_value CBOR value item. + * @param[in,out] first_p First top-level/parent sibling. + * @param[out] node_p Pointer to the created node. + * @return LY_ERR value. + */ static LY_ERR -lydcbor_parse_list(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) +lydcbor_parse_attribute(struct lyd_cbor_ctx *lydctx, struct lyd_node *attr_node, const struct lysc_node *snode, + const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, + const cbor_item_t *cbor_value, struct lyd_node **first_p, struct lyd_node **node_p) { - LY_ERR ret = LY_SUCCESS; + LY_ERR r; + const char *opaq_name, *mod_name, *attr_mod; + size_t opaq_name_len, attr_mod_len; - if (cbor_isa_array(cbor_value)) + if (!attr_node) { - ret = lydcbor_parse_list_array(lydctx, snode, cbor_value, first_p, parsed); - } - else if (cbor_isa_map(cbor_value)) - { - /* Single list entry */ - struct lyd_node *node = NULL; - - ret = lyd_create_inner(snode, &node); - LY_CHECK_RET(ret); - - lyd_hash(node); - /* Insert into tree */ - ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) + /* learn the attribute module name */ + if (!snode) { - lyd_free_tree(node); - return ret; + if (!prefix) + { + lydcbor_get_node_prefix(parent, NULL, 0, &attr_mod, &attr_mod_len); + } + else + { + attr_mod = prefix; + attr_mod_len = prefix_len; + } } - /* Add to parsed set */ - ret = ly_set_add(parsed, node, 1, NULL); - LY_CHECK_RET(ret); - - /* Parse list entry content */ - struct lyd_node *child_first = NULL; - ret = lydcbor_subtree_r(lydctx, node, &child_first, parsed, cbor_value); - if (ret) + /* try to find the instance */ + LY_LIST_FOR(parent ? lyd_child(parent) : *first_p, attr_node) { - return ret; - } + if (snode) + { + if (attr_node->schema) + { + if (attr_node->schema == snode) + { + break; + } + } + else + { + mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name; + if (!strcmp(LYD_NAME(attr_node), snode->name) && mod_name && !strcmp(mod_name, snode->module->name)) + { + break; + } + } + } + else + { + if (attr_node->schema) + { + mod_name = attr_node->schema->module->name; + } + else + { + mod_name = ((struct lyd_node_opaq *)attr_node)->name.module_name; + } - /* Link children to list entry */ - if (child_first) - { - lyd_insert_child(node, child_first); + if (!ly_strncmp(LYD_NAME(attr_node), name, name_len) && mod_name && attr_mod && + !ly_strncmp(mod_name, attr_mod, attr_mod_len)) + { + break; + } + } } } - else - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "List \"%s\" value must be a CBOR map or array", snode->name); - ret = LY_EVALID; - } - return ret; -} + if (!attr_node) + { + /* parse as an opaq node with @ prefix */ + uint32_t prev_opts; -static LY_ERR -lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *cbor_value, struct lyd_node **first_p, struct ly_set *parsed) -{ - LY_ERR ret; - struct lyd_node *node = NULL; + prev_opts = lydctx->parse_opts; + lydctx->parse_opts &= ~LYD_PARSE_STRICT; + lydctx->parse_opts |= LYD_PARSE_OPAQ; - ret = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, &node); - LY_CHECK_RET(ret); + opaq_name = prefix ? prefix - 1 : name - 1; + opaq_name_len = prefix ? prefix_len + name_len + 2 : name_len + 1; + r = lydcbor_parse_opaq(lydctx, opaq_name, opaq_name_len, NULL, 0, parent, cbor_value, first_p, node_p); - if (!node) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create any node for \"%s\"", snode->name); - return LY_EVALID; + lydctx->parse_opts = prev_opts; + LY_CHECK_RET(r); } - - lyd_hash(node); - /* Insert into tree */ - ret = lyd_insert_sibling(*first_p, node, first_p); - if (ret) + else { - lyd_free_tree(node); - return ret; + LY_CHECK_RET(lydcbor_meta_attr(lydctx, snode, attr_node, cbor_value)); } - /* Add to parsed set */ - return ly_set_add(parsed, node, 1, NULL); -} - - -/** - * @brief Parse CBOR metadata/attributes. - * - * @param[in] lydctx CBOR parser context. - * @param[in] cbor_item CBOR item containing metadata. - * @param[in,out] node Data node to attach metadata to. - * @return LY_ERR value. - */ -static LY_ERR -lydcbor_parse_metadata(struct lyd_cbor_ctx *lydctx, const void *cbor_item, struct lyd_node *node) -{ - /* Future implementation for CBOR metadata parsing */ - (void)lydctx; - (void)cbor_item; - (void)node; return LY_SUCCESS; } - /** - * @brief Get module prefix from a qualified name. + * @brief Parse a single anydata/anyxml node from CBOR. * - * @param[in] qname Qualified name (prefix:name or just name). - * @param[in] qname_len Length of the qualified name. - * @param[out] prefix Extracted prefix (points into qname, not allocated). - * @param[out] prefix_len Length of the prefix. - * @param[out] name Local name (points into qname, not allocated). - * @param[out] name_len Length of the local name. - * @return LY_SUCCESS on success. + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node corresponding to the member. + * @param[in] ext Extension instance of @p snode, if any. + * @param[in] cbor_value CBOR value item. + * @param[out] node Parsed data (or opaque) node. + * @return LY_SUCCESS if a node was successfully parsed. + * @return LY_ENOT in case of invalid CBOR encoding. + * @return LY_ERR on other errors. */ static LY_ERR -lydcbor_parse_qname(const char *qname, size_t qname_len, const char **prefix, size_t *prefix_len, - const char **name, size_t *name_len) +lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, + const cbor_item_t *cbor_value, struct lyd_node **node) { - const char *colon; + LY_ERR r, rc = LY_SUCCESS; + uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts; + struct lyd_node *child = NULL; + ly_bool log_node = 0; + enum cbor_type type = cbor_typeof(cbor_value); - assert(qname && name && name_len); + assert(snode->nodetype & LYD_NODE_ANY); - *name = qname; - *name_len = qname_len; + *node = NULL; - if (prefix) + /* status check according to allowed CBOR types */ + if (snode->nodetype == LYS_ANYXML) { - *prefix = NULL; + LY_CHECK_RET((type != CBOR_TYPE_MAP) && (type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && + (type != CBOR_TYPE_NEGINT) && (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL), + LY_ENOT); } - if (prefix_len) + else { - *prefix_len = 0; + LY_CHECK_RET(type != CBOR_TYPE_MAP, LY_ENOT); } - /* Look for module prefix separator */ - colon = ly_strnchr(qname, ':', qname_len); - if (colon) + /* create any node */ + if (type == CBOR_TYPE_MAP) { - /* We have a module prefix */ - if (prefix) - { - *prefix = qname; - *prefix_len = colon - qname; - } + /* create node */ + r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + assert(*node); + LOG_LOCSET(NULL, *node); + log_node = 1; + + /* parse data tree with correct options */ + lydctx->parse_opts &= ~LYD_PARSE_STRICT; + lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0); + lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS; + lydctx->any_schema = snode; + + /* process the anydata content */ + r = lydcbor_subtree_r(lydctx, NULL, &child, NULL, cbor_value); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - /* Local name starts after the colon */ - *name = colon + 1; - *name_len = qname_len - (colon - qname) - 1; + /* finish linking metadata */ + r = lydcbor_metadata_finish(lydctx, &child); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - /* Validate we have both prefix and name */ - if ((colon == qname) || (*name_len == 0)) - { - return LY_EVALID; - } + /* assign the data tree */ + ((struct lyd_node_any *)*node)->value.tree = child; + child = NULL; + } + else + { + /* store CBOR value directly */ + r = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } - return LY_SUCCESS; +cleanup: + if (log_node) + { + LOG_LOCBACK(0, 1); + } + lydctx->parse_opts = prev_parse_opts; + lydctx->int_opts = prev_int_opts; + lydctx->any_schema = NULL; + lyd_free_tree(child); + return rc; } /** - * @brief Get schema node from CBOR node name, following lydjson_get_snode logic. + * @brief Parse a single instance of an inner node from CBOR. * - * @param[in] lydctx CBOR parser context. - * @param[in] name Node name. - * @param[in] name_len Length of node name. - * @param[in] parent Data parent node. - * @param[out] snode Schema node found. - * @param[out] ext Extension instance if found. - * @return LY_ERR value. + * @param[in] lydctx CBOR data parser context. + * @param[in] snode Schema node corresponding to the member. + * @param[in] ext Extension instance of @p snode, if any. + * @param[in] cbor_value CBOR value item. + * @param[out] node Parsed data (or opaque) node. + * @return LY_SUCCESS if a node was successfully parsed. + * @return LY_ENOT in case of invalid CBOR encoding. + * @return LY_ERR on other errors. */ static LY_ERR -lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_len, - struct lyd_node *parent, const struct lysc_node **snode, - const struct lysc_ext_instance **ext) +lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, + const cbor_item_t *cbor_value, struct lyd_node **node) { - LY_ERR ret = LY_SUCCESS, r; - const char *prefix = NULL, *local_name = NULL; - size_t prefix_len = 0, local_name_len = 0; - const struct lys_module *mod = NULL; - const struct lysc_node *sparent = NULL; - uint32_t getnext_opts; + LY_ERR r, rc = LY_SUCCESS; + uint32_t prev_parse_opts = lydctx->parse_opts; + + LY_CHECK_RET(cbor_typeof(cbor_value) != CBOR_TYPE_MAP, LY_ENOT); + + /* create inner node */ + LY_CHECK_RET(lyd_create_inner(snode, node)); + + /* use it for logging */ + LOG_LOCSET(NULL, *node); - assert(lydctx && name && snode); - *snode = NULL; if (ext) { - *ext = NULL; + /* only parse these extension data and validate afterwards */ + lydctx->parse_opts |= LYD_PARSE_ONLY; } - /* Parse qualified name */ - LY_CHECK_RET(lydcbor_parse_qname(name, name_len, &prefix, &prefix_len, &local_name, &local_name_len)); + /* process children */ + r = lydcbor_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL, cbor_value); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + /* finish linking metadata */ + r = lydcbor_metadata_finish(lydctx, lyd_node_child_p(*node)); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - /* Get parent schema node */ - if (parent) + if (snode->nodetype == LYS_LIST) { - sparent = parent->schema; - if (!sparent) - { - /* Opaque parent */ - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Cannot parse \"%.*s\" node with opaque parent.", - (int)local_name_len, local_name); - ret = LY_EVALID; - goto cleanup; - } + /* check all keys exist */ + r = lyd_parser_check_keys(*node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } - else + + if (!(lydctx->parse_opts & LYD_PARSE_ONLY) && !rc) { - sparent = NULL; + /* new node validation */ + r = lyd_parser_validate_new_implicit((struct lyd_ctx *)lydctx, *node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } - /* Resolve module if prefix is present */ - if (prefix) +cleanup: + lydctx->parse_opts = prev_parse_opts; + LOG_LOCBACK(0, 1); + if (!(*node)->hash) { - mod = ly_ctx_get_module_implemented2(lydctx->cborctx->ctx, prefix, prefix_len); - if (!mod) - { - if (lydctx->parse_opts & LYD_PARSE_STRICT) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); - ret = LY_EVALID; - goto cleanup; - } - if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown module \"%.*s\".", (int)prefix_len, prefix); - ret = LY_EVALID; - goto cleanup; - } - } + /* list without keys is unusable */ + lyd_free_tree(*node); + *node = NULL; } - else if (!sparent) - { - /* Top-level node without prefix - need to find module */ - /* Try to find the node in all implemented modules */ - const struct lys_module *iter_mod; - uint32_t idx = 0; - ly_bool found = 0; - - while ((iter_mod = ly_ctx_get_module_iter(lydctx->cborctx->ctx, &idx))) - { - if (!iter_mod->implemented) - { - continue; - } + return rc; +} - /* Check if node exists in this module */ - if (lys_find_child(NULL, iter_mod, local_name, local_name_len, 0, 0)) - { - if (found) - { - /* Ambiguous name */ - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Ambiguous node name \"%.*s\", use module prefix.", - (int)local_name_len, local_name); - ret = LY_EVALID; - goto cleanup; - } - mod = iter_mod; - found = 1; - } - } +/** + * @brief Parse a single instance of a node from CBOR. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] parent Data parent of the subtree. + * @param[in,out] first_p Pointer to the variable holding the first top-level sibling. + * @param[in] snode Schema node corresponding to the member. + * @param[in] ext Extension instance of @p snode, if any. + * @param[in] name Parsed CBOR node name. + * @param[in] name_len Length of @p name. + * @param[in] prefix Parsed CBOR node prefix. + * @param[in] prefix_len Length of @p prefix. + * @param[in] cbor_value CBOR value item. + * @param[out] node Parsed data (or opaque) node. + * @return LY_SUCCESS if a node was successfully parsed. + * @return LY_ENOT in case of invalid CBOR encoding. + * @return LY_ERR on other errors. + */ +static LY_ERR +lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, + const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len, + const char *prefix, size_t prefix_len, const cbor_item_t *cbor_value, struct lyd_node **node) +{ + LY_ERR r, rc = LY_SUCCESS; + uint32_t type_hints = 0; + char *str_val = NULL; + size_t str_len = 0; - if (!found && !(lydctx->parse_opts & LYD_PARSE_OPAQ)) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unknown node \"%.*s\".", (int)local_name_len, local_name); - ret = LY_EVALID; - goto cleanup; - } - } + LOG_LOCSET(snode, NULL); - /* Set getnext options */ - getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; - if (parent && (parent->schema->nodetype & (LYS_RPC | LYS_ACTION))) + r = lydcbor_data_check_opaq(lydctx, snode, cbor_value, &type_hints); + if (r == LY_SUCCESS) { - if (lydctx->int_opts & LYD_INTOPT_RPC) + assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY)); + if (lydcbor_is_null(cbor_value)) { - getnext_opts = 0; + /* do not do anything if value is CBOR null */ + goto cleanup; } - else if (lydctx->int_opts & LYD_INTOPT_REPLY) + else if (snode->nodetype & LYD_NODE_TERM) { - getnext_opts = LYS_GETNEXT_OUTPUT; - } - } - - /* Find schema node */ - if (sparent) - { - /* Search in parent's children */ - *snode = lys_find_child(sparent, sparent->module, local_name, local_name_len, 0, getnext_opts); + enum cbor_type type = cbor_typeof(cbor_value); - /* Try to find extension data if regular node not found */ - if (!*snode && ext) - { - r = ly_nested_ext_schema(parent, sparent, prefix, prefix_len, LY_VALUE_JSON, NULL, - local_name, local_name_len, snode, ext); - if (r != LY_ENOT) + if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) { - if (r) + cbor_item_t **handle = cbor_array_handle(cbor_value); + if (lydcbor_is_null(handle[0])) { - ret = r; + /* [null] case */ goto cleanup; } } - } - } - else - { - /* Top-level node */ - if (mod) - { - /* Search in specific module */ - *snode = lys_find_child(NULL, mod, local_name, local_name_len, 0, getnext_opts); - } - /* Extension data for top-level not typically handled */ - } - /* Handle missing schema node */ - if (!*snode) - { - if (lydctx->parse_opts & LYD_PARSE_STRICT) - { - if (prefix) + if ((type != CBOR_TYPE_ARRAY) && (type != CBOR_TYPE_UINT) && (type != CBOR_TYPE_NEGINT) && + (type != CBOR_TYPE_STRING) && (type != CBOR_TYPE_FLOAT_CTRL)) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", - (int)local_name_len, local_name, (int)prefix_len, prefix); - } - else - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", - (int)local_name_len, local_name); + rc = LY_ENOT; + goto cleanup; } - ret = LY_EVALID; - goto cleanup; + + /* create terminal node */ + r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, str_val, str_len, NULL, LY_VALUE_CBOR, + NULL, type_hints, node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } - else if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) + else if (snode->nodetype & LYD_NODE_INNER) { - /* Log error but continue if not in strict mode and opaque allowed */ - if (prefix) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\" in module \"%.*s\".", - (int)local_name_len, local_name, (int)prefix_len, prefix); - } - else - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Unknown element \"%.*s\".", - (int)local_name_len, local_name); - } - ret = LY_EVALID; - goto cleanup; + /* create inner node */ + r = lydcbor_parse_instance_inner(lydctx, snode, ext, cbor_value, node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } - /* If opaque parsing allowed, *snode remains NULL and caller handles it */ - } - -cleanup: - return ret; -} - -static LY_ERR -lydcbor_parse_leaflist_array(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, - const cbor_item_t *array_item, struct lyd_node **first_p, struct ly_set *parsed) -{ - LY_ERR ret = LY_SUCCESS; - struct lyd_node *node = NULL; - size_t array_size; - cbor_item_t **array_handle; - - assert(lydctx && snode && array_item && parsed); - - if (!cbor_isa_array(array_item)) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR array for leaf-list"); - return LY_EVALID; - } - - if (snode->nodetype != LYS_LEAFLIST) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Schema node must be leaf-list"); - return LY_EVALID; - } + else + { + /* create any node */ + r = lydcbor_parse_any(lydctx, snode, ext, cbor_value, node); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + LY_CHECK_GOTO(!*node, cleanup); - array_size = cbor_array_size(array_item); - array_handle = cbor_array_handle(array_item); + /* add/correct flags */ + r = lyd_parser_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (!array_handle && array_size > 0) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR array structure"); - return LY_EVALID; + if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) + { + /* store for ext instance node validation, if needed */ + r = lyd_validate_node_ext(*node, &lydctx->ext_node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } } - - for (size_t i = 0; i < array_size; ++i) + else if (r == LY_ENOT) { - const cbor_item_t *item = array_handle[i]; + /* parse it again as an opaq node */ + r = lydcbor_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, cbor_value, first_p, node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (!item) + if (snode->nodetype == LYS_LIST) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null array element at index %zu", i); - ret = LY_EVALID; - goto cleanup; + ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST; } - - LY_CHECK_GOTO(ret = lydcbor_parse_node_value(lydctx, snode, &node, item), cleanup); - - if (!node) + else if (snode->nodetype == LYS_LEAFLIST) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Failed to create node for array element %zu", i); - ret = LY_EVALID; - goto cleanup; + ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST; } - - lyd_hash(node); - /* Insert the node */ - ret = lyd_insert_sibling(*first_p, node, first_p); - LY_CHECK_GOTO(ret, cleanup); - - /* Add to parsed set */ - LY_CHECK_GOTO(ret = ly_set_add(parsed, node, 1, NULL), cleanup); - node = NULL; /* Reset pointer after successful insertion */ } - -cleanup: - if (ret && node) + else { - lyd_free_tree(node); + /* error */ + rc = r; + goto cleanup; } - return ret; + +cleanup: + free(str_val); + LOG_LOCBACK(1, 0); + return rc; } +/** + * @brief Parse CBOR subtree. All leaf-list and list instances of a node are considered one subtree. + * + * @param[in] lydctx CBOR data parser context. + * @param[in] parent Data parent of the subtree, must be set if @p first_p is not. + * @param[in,out] first_p Pointer to the variable holding the first top-level sibling. + * @param[in,out] parsed Optional set to add all the parsed siblings into. + * @return LY_ERR value. + */ static LY_ERR -lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_set *parsed, const cbor_item_t *cbor_obj) +lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, + struct ly_set *parsed, const cbor_item_t *cbor_obj) { - LY_ERR ret = LY_SUCCESS; - - printf("Entering lydcbor_subtree_r\n"); - printf("CBOR object:\n"); - print_json(cbor_obj); - printf("\n"); - + LY_ERR r, rc = LY_SUCCESS; + const char *name, *prefix = NULL, *expected = NULL; + size_t name_len, prefix_len = 0; + ly_bool is_meta = 0; const struct lysc_node *snode = NULL; - char *key_str = NULL; - size_t key_len = 0; + struct lysc_ext_instance *ext = NULL; + struct lyd_node *node = NULL, *attr_node = NULL; + const struct ly_ctx *ctx = lydctx->cborctx->ctx; + struct cbor_pair *pairs; + size_t map_size; - assert(lydctx && first_p && parsed && cbor_obj); + assert(parent || first_p); + assert(cbor_typeof(cbor_obj) == CBOR_TYPE_MAP); - size_t map_size = cbor_map_size(cbor_obj); - struct cbor_pair *pairs = cbor_map_handle(cbor_obj); + map_size = cbor_map_size(cbor_obj); + pairs = cbor_map_handle(cbor_obj); if (!pairs && map_size > 0) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid CBOR map structure"); + LOGVAL(ctx, LYVE_SYNTAX, "Invalid CBOR map structure"); return LY_EVALID; } + /* process all members */ for (size_t i = 0; i < map_size; ++i) { const cbor_item_t *key_item = pairs[i].key; const cbor_item_t *value_item = pairs[i].value; + char *key_str = NULL; + size_t key_len = 0; if (!key_item || !value_item) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Null key or value at map index %zu", i); - ret = LY_EVALID; + LOGVAL(ctx, LYVE_SYNTAX, "Null key or value in CBOR map"); + rc = LY_EVALID; goto cleanup; } - if (lydcbor_is_null(value_item)) { - // Skip null values - don't create any nodes - continue; // or return LY_SUCCESS depending on your loop structure + /* Skip null values */ + if (lydcbor_is_null(value_item)) + { + continue; } /* Get key string */ - LY_CHECK_GOTO(ret = lydcbor_get_key_string(lydctx, key_item, &key_str, &key_len), cleanup); + if (!cbor_isa_string(key_item)) + { + LOGVAL(ctx, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); + rc = LY_EVALID; + goto cleanup; + } + + LY_CHECK_ERR_GOTO(rc = lydcbor_item_to_string(key_item, &key_str, &key_len), free(key_str), cleanup); - /* Find schema node */ - LY_CHECK_GOTO(ret = lydcbor_get_snode(lydctx, key_str, key_len, - parent, &snode, NULL), - cleanup); + /* process the node name */ + lydcbor_parse_name(key_str, key_len, &name, &name_len, &prefix, &prefix_len, &is_meta); - /* Handle different node types */ - if (snode->nodetype & (LYS_LEAF | LYS_LEAFLIST)) + if (!is_meta || name_len || prefix_len) { - if (snode->nodetype == LYS_LEAFLIST && cbor_isa_array(value_item)) + /* get the schema node */ + r = lydcbor_get_snode(lydctx, is_meta, prefix, prefix_len, name, name_len, parent, &snode, &ext); + if (r == LY_ENOT) { - ret = lydcbor_parse_leaflist_array(lydctx, snode, value_item, first_p, parsed); + free(key_str); + continue; } - else + else if ((r == LY_EVALID) && (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR)) + { + rc = r; + free(key_str); + continue; + } + else if (r) { - ret = lydcbor_parse_terminal(lydctx, snode, value_item, first_p, parsed); + rc = r; + free(key_str); + goto cleanup; } } - else if (snode->nodetype == LYS_CONTAINER) + + if (is_meta) { - ret = lydcbor_parse_container(lydctx, snode, value_item, first_p, parsed); + /* parse as metadata */ + if (!name_len && !prefix_len && !parent) + { + LOGVAL(ctx, LYVE_SYNTAX, + "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); + r = LY_EVALID; + free(key_str); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + else if (!name_len && !prefix_len) + { + /* parent's metadata without a name */ + attr_node = parent; + snode = attr_node->schema; + } + r = lydcbor_parse_attribute(lydctx, attr_node, snode, name, name_len, prefix, prefix_len, parent, + value_item, first_p, &node); + free(key_str); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } - else if (snode->nodetype == LYS_LIST) + else if (!snode) { - ret = lydcbor_parse_list(lydctx, snode, value_item, first_p, parsed); + if (!(lydctx->parse_opts & LYD_PARSE_OPAQ)) + { + /* skip element */ + free(key_str); + continue; + } + else + { + /* parse as an opaq node */ + if (name_len == 0) + { + LOGVAL(ctx, LYVE_SYNTAX, "CBOR object member name cannot be a zero-length string."); + r = LY_EVALID; + free(key_str); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + + /* parse opaq */ + r = lydcbor_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, value_item, first_p, &node); + free(key_str); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } } - else if (snode->nodetype & (LYS_ANYDATA | LYS_ANYXML)) + else { - ret = lydcbor_parse_any(lydctx, snode, value_item, first_p, parsed); + /* parse as a standard lyd_node but it can still turn out to be an opaque node */ + + /* set expected representation */ + switch (snode->nodetype) + { + case LYS_LEAFLIST: + expected = "name/array of values"; + break; + case LYS_LIST: + expected = "name/array of objects"; + break; + case LYS_LEAF: + expected = "name/value"; + break; + case LYS_CONTAINER: + case LYS_NOTIF: + case LYS_ACTION: + case LYS_RPC: + case LYS_ANYDATA: + expected = "name/object"; + break; + case LYS_ANYXML: + expected = "name/value"; + break; + } + + /* check the representation and process */ + enum cbor_type value_type = cbor_typeof(value_item); + + switch (snode->nodetype) + { + case LYS_LEAFLIST: + case LYS_LIST: + if (value_type != CBOR_TYPE_ARRAY) + { + goto representation_error; + } + + /* process all values/objects in array */ + size_t array_size = cbor_array_size(value_item); + cbor_item_t **array_handle = cbor_array_handle(value_item); + + for (size_t j = 0; j < array_size; ++j) + { + const cbor_item_t *item = array_handle[j]; + + r = lydcbor_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len, + item, &node); + if (r == LY_ENOT) + { + free(key_str); + goto representation_error; + } + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + lydcbor_maintain_children(parent, first_p, &node, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + } + break; + case LYS_LEAF: + case LYS_CONTAINER: + case LYS_NOTIF: + case LYS_ACTION: + case LYS_RPC: + case LYS_ANYDATA: + case LYS_ANYXML: + /* process the value/object */ + r = lydcbor_parse_instance(lydctx, parent, first_p, snode, ext, name, name_len, prefix, prefix_len, + value_item, &node); + if (r == LY_ENOT) + { + free(key_str); + goto representation_error; + } + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + if (snode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) + { + /* remember the RPC/action/notification */ + lydctx->op_node = node; + } + break; + } } - else + + free(key_str); + + /* remember a successfully parsed node */ + if (parsed && node) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Invalid schema node type %d for \"%s\"", - snode->nodetype, snode->name); - ret = LY_EVALID; + ly_set_add(parsed, node, 1, NULL); } - LY_CHECK_GOTO(ret, cleanup); + /* finally connect the parsed node */ + lydcbor_maintain_children(parent, first_p, &node, + lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); + } - free(key_str); - key_str = NULL; + /* success */ + goto cleanup; + +representation_error: + LOGVAL(ctx, LYVE_SYNTAX, "Expecting CBOR %s but %s \"%s\" is represented in input data differently.", + expected, lys_nodetype2str(snode->nodetype), snode->name); + rc = LY_EVALID; + if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) + { + /* try to skip the invalid data */ + if ((r = lydcbor_data_skip(lydctx->cborctx))) + { + rc = r; + } } cleanup: - free(key_str); - return ret; + lyd_free_tree(node); + return rc; } /** - * @brief Create a new CBOR parser context. + * @brief Common start of CBOR parser processing different types of input data. * * @param[in] ctx libyang context. - * @param[in] ext Extension instance providing context for the top level element, NULL if none. - * @param[in] parse_opts Parse options, see @ref dataparseroptions. - * @param[in] val_opts Validation options, see @ref datavalidationoptions. - * @param[in] format CBOR format variant (named or SID). - * @param[out] lydctx_p Pointer to the created CBOR parser context. + * @param[in] in Input structure. + * @param[in] parse_opts Options for parser. + * @param[in] val_opts Options for validation phase. + * @param[out] lydctx_p Data parser context to finish validation. * @return LY_ERR value. */ static LY_ERR -lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, - uint32_t val_opts, struct lyd_cbor_ctx **lydctx_p) +lyd_parse_cbor_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, + struct lyd_cbor_ctx **lydctx_p) { LY_ERR ret = LY_SUCCESS; struct lyd_cbor_ctx *lydctx; @@ -1162,19 +1998,14 @@ lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts lydctx->val_opts = val_opts; lydctx->free = lyd_cbor_ctx_free; - /* Create low-level CBOR context (includes CBOR parsing) */ - LY_CHECK_ERR_RET(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), free(lydctx), ret); + /* Create low-level CBOR context */ + LY_CHECK_ERR_RET(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), free(lydctx), ret); cbortype = cbor_typeof(lydctx->cborctx->cbor_data); - /* assuming that the top level structure is always a map - - though this is not mentioned explicitly in RFC9254 - it is implied - and it is almost always the case - This is a similar assumption made - to the RFC 7951 where JSON Encoding of data modeled by YANG is always assumed - to a have a top-level structure as an object */ if (!cbor_isa_map(lydctx->cborctx->cbor_data)) { /* expecting top-level map */ - LOGVAL(ctx, LYVE_SYNTAX_CBOR, "Expected top-level CBOR map, but %s found.", lycbor_token2str(cbortype)); + LOGVAL(ctx, LYVE_SYNTAX, "Expected top-level CBOR map, but %d found.", cbortype); *lydctx_p = NULL; lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); return LY_EVALID; @@ -1191,40 +2022,67 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st { LY_ERR r, rc = LY_SUCCESS; struct lyd_cbor_ctx *lydctx = NULL; - printf("Entering lyd_parse_cbor\n AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\n"); - /* Initialize context (CBOR parsing happens in lycbor_ctx_new) */ - rc = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, &lydctx); + rc = lyd_parse_cbor_init(ctx, in, parse_opts, val_opts, &lydctx); LY_CHECK_GOTO(rc, cleanup); lydctx->int_opts = int_opts; lydctx->ext = ext; + // DEBUG: print the parsed CBOR data + printf("DEBUG: Parsed CBOR data:\n"); + print_json(lydctx->cborctx->cbor_data); + printf("\n"); + // END DEBUG + /* find the operation node if it exists already */ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); - /* Parse the CBOR structure - read subtrees */ + /* read subtree(s) */ r = lydcbor_subtree_r(lydctx, parent, first_p, parsed, lydctx->cborctx->cbor_data); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - /* Unexpected sibling node error handling */ - - /* Validate operation node presence */ - if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && - !lydctx->op_node) { + if ((int_opts & LYD_INTOPT_NO_SIBLINGS)) + { + LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node."); + r = LY_EVALID; + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + } + if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) + { LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } - /* also need to deal with metadata linking etc*/ + + /* finish linking metadata */ + r = lydcbor_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + if (parse_opts & LYD_PARSE_SUBTREE) + { + /* subtree parsing not fully implemented for CBOR */ + if (subtree_sibling) + { + *subtree_sibling = 0; + } + } cleanup: - if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { + /* there should be no unresolved types stored */ + assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count && !lydctx->node_when.count)); + + if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) + { lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); lydctx = NULL; } - - *lydctx_p = (struct lyd_ctx *)lydctx; + else + { + *lydctx_p = (struct lyd_ctx *)lydctx; + lycbor_ctx_free(lydctx->cborctx); + lydctx->cborctx = NULL; + } return rc; } diff --git a/src/parser_json.c b/src/parser_json.c index af0ec64b6..c0dc01568 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -307,6 +307,7 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref } /* unknown data node */ + printf("checkpoint1-json\n"); if (lydctx->parse_opts & LYD_PARSE_STRICT) { if (parent) { LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", diff --git a/src/plugins_types.c b/src/plugins_types.c index a634a38dc..3f5819d28 100644 --- a/src/plugins_types.c +++ b/src/plugins_types.c @@ -151,6 +151,7 @@ ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, void *prefix prefix = ly_xml_get_prefix(mod, prefix_data); break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: prefix = ly_json_get_prefix(mod, prefix_data); @@ -755,6 +756,7 @@ lyplg_type_lypath_new(const struct ly_ctx *ctx, const char *value, uint32_t valu break; case LY_VALUE_CANON: case LY_VALUE_LYB: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_STR_NS: prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT; diff --git a/src/plugins_types/instanceid.c b/src/plugins_types/instanceid.c index 4fe44281e..28fe4fe74 100644 --- a/src/plugins_types/instanceid.c +++ b/src/plugins_types/instanceid.c @@ -71,6 +71,7 @@ instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, void *pr inherit_prefix = 0; break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/plugins_types/instanceid_keys.c b/src/plugins_types/instanceid_keys.c index 203f61789..eb182d2ec 100644 --- a/src/plugins_types/instanceid_keys.c +++ b/src/plugins_types/instanceid_keys.c @@ -195,6 +195,7 @@ lyplg_type_store_instanceid_keys(const struct ly_ctx *ctx, const struct lysc_typ switch (format) { case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/plugins_types/node_instanceid.c b/src/plugins_types/node_instanceid.c index b946c3341..eccb8b870 100644 --- a/src/plugins_types/node_instanceid.c +++ b/src/plugins_types/node_instanceid.c @@ -75,6 +75,7 @@ node_instanceid_path2str(const struct ly_path *path, LY_VALUE_FORMAT format, voi inherit_prefix = 0; break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: @@ -203,6 +204,7 @@ lyplg_type_store_node_instanceid(const struct ly_ctx *ctx, const struct lysc_typ break; case LY_VALUE_CANON: case LY_VALUE_LYB: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_STR_NS: prefix_opt = LY_PATH_PREFIX_STRICT_INHERIT; diff --git a/src/plugins_types/xpath1.0.c b/src/plugins_types/xpath1.0.c index 5aa55c173..a83b98a5e 100644 --- a/src/plugins_types/xpath1.0.c +++ b/src/plugins_types/xpath1.0.c @@ -296,6 +296,7 @@ lyplg_type_store_xpath10(const struct ly_ctx *ctx, const struct lysc_type *type, switch (format) { case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: diff --git a/src/printer_json.c b/src/printer_json.c index 5864fb8c5..949557e49 100644 --- a/src/printer_json.c +++ b/src/printer_json.c @@ -170,6 +170,7 @@ node_prefix(const struct lyd_node *node, const struct lysc_node *snode, const ch onode = (struct lyd_node_opaq *)node; switch (onode->format) { + case LY_VALUE_CBOR: case LY_VALUE_JSON: *mod_name = onode->name.module_name; if (data_dict) { @@ -342,6 +343,7 @@ json_print_member2(struct jsonpr_ctx *pctx, const struct lyd_node *parent, LY_VA /* determine prefix string */ if (name) { switch (format) { + case LY_VALUE_CBOR: case LY_VALUE_JSON: module_name = name->module_name; break; diff --git a/src/printer_lyb.c b/src/printer_lyb.c index 1088f5309..16a368a75 100644 --- a/src/printer_lyb.c +++ b/src/printer_lyb.c @@ -753,6 +753,7 @@ lyb_print_prefix_data(LY_VALUE_FORMAT format, const void *prefix_data, struct ly LY_CHECK_RET(lyb_write_string(ns->uri, 0, lybctx)); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: /* nothing to print */ diff --git a/src/printer_xml.c b/src/printer_xml.c index e04a2aaac..07d7dde78 100644 --- a/src/printer_xml.c +++ b/src/printer_xml.c @@ -129,6 +129,7 @@ xml_print_ns_opaq(struct xmlpr_ctx *pctx, LY_VALUE_FORMAT format, const struct l return xml_print_ns(pctx, name->module_ns, (prefix_opts & LYXML_PREFIX_DEFAULT) ? NULL : name->prefix, prefix_opts); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: if (name->module_name) { mod = ly_ctx_get_module_latest(pctx->ctx, name->module_name); diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c index ae038147e..db989c215 100644 --- a/src/schema_compile_node.c +++ b/src/schema_compile_node.c @@ -3022,6 +3022,7 @@ lysc_resolve_schema_nodeid(struct lysc_ctx *ctx, const char *nodeid, size_t node /* use the current module */ mod = ctx->cur_mod; break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: if (!ctx_node) { diff --git a/src/tree.h b/src/tree.h index 9e2b7fa8d..309c6cb1e 100644 --- a/src/tree.h +++ b/src/tree.h @@ -237,7 +237,7 @@ typedef enum { LY_VALUE_SCHEMA_RESOLVED, /**< resolved YANG schema value, prefixes map to module structures directly */ LY_VALUE_XML, /**< XML data value, prefixes map to XML namespace prefixes */ LY_VALUE_JSON, /**< JSON data value, prefixes map to module names */ - LY_VALUE_CBOR, /**< CBOR data value, prefixes map to module names */ + LY_VALUE_CBOR, /**< CBOR data value, prefixes map to module names (same as JSON) */ LY_VALUE_LYB, /**< LYB data binary value, prefix mapping is type-specific (but usually like JSON) */ LY_VALUE_STR_NS /**< any data format value, prefixes map to XML namespace prefixes */ } LY_VALUE_FORMAT; diff --git a/src/tree_data_common.c b/src/tree_data_common.c index 4baec5720..945997ea2 100644 --- a/src/tree_data_common.c +++ b/src/tree_data_common.c @@ -317,6 +317,7 @@ lyd_owner_module(const struct lyd_node *node) return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: if (opaq->name.module_name) { return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name); @@ -351,6 +352,7 @@ lyd_node_module(const struct lyd_node *node) return ly_ctx_get_module_implemented_ns(LYD_CTX(node), opaq->name.module_ns); } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: if (opaq->name.module_name) { return ly_ctx_get_module_implemented(LYD_CTX(node), opaq->name.module_name); @@ -895,6 +897,7 @@ lyd_parse_opaq_error(const struct lyd_node *node) mod = sparent->module; } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: if (!sparent || strcmp(opaq->name.module_name, sparent->module->name)) { @@ -1248,6 +1251,7 @@ ly_free_prefix_data(LY_VALUE_FORMAT format, void *prefix_data) break; case LY_VALUE_CANON: case LY_VALUE_SCHEMA: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: break; @@ -1307,6 +1311,7 @@ ly_dup_prefix_data(const struct ly_ctx *ctx, LY_VALUE_FORMAT format, const void } break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: assert(!prefix_data); @@ -1432,6 +1437,7 @@ ly_store_prefix_data(const struct ly_ctx *ctx, const void *value, uint32_t value break; case LY_VALUE_CANON: case LY_VALUE_SCHEMA_RESOLVED: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: if (!*prefix_data_p) { @@ -1462,6 +1468,8 @@ ly_format2str(LY_VALUE_FORMAT format) return "schema stored mapping"; case LY_VALUE_XML: return "XML prefixes"; + case LY_VALUE_CBOR: + return "CBOR module names"; case LY_VALUE_JSON: return "JSON module names"; case LY_VALUE_LYB: diff --git a/src/tree_data_new.c b/src/tree_data_new.c index 20dfd1ad1..b69a66483 100644 --- a/src/tree_data_new.c +++ b/src/tree_data_new.c @@ -854,6 +854,7 @@ lyd_new_meta2(const struct ly_ctx *ctx, struct lyd_node *parent, uint32_t option return LY_ENOTFOUND; } break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: mod = ly_ctx_get_module_implemented(ctx, attr->name.module_name); if (!mod) { diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h index bc14510b2..d241450d0 100644 --- a/src/tree_schema_internal.h +++ b/src/tree_schema_internal.h @@ -676,6 +676,7 @@ char *lysc_path_until(const struct lysc_node *node, const struct lysc_node *pare * LY_VALUE_SCHEMA - const struct ::lysp_module* (module used for resolving imports to prefixes) * LY_VALUE_SCHEMA_RESOLVED - struct ::lysc_prefix* (sized array of pairs: prefix - module) * LY_VALUE_XML - struct ::ly_set* (set of all returned modules as struct ::lys_module) + * LY_VALUE_CBOR - NULL * LY_VALUE_JSON - NULL * LY_VALUE_LYB - NULL * @return Module prefix to print. @@ -695,6 +696,7 @@ const char *ly_get_prefix(const struct lys_module *mod, LY_VALUE_FORMAT format, * LY_VALUE_SCHEMA - const struct lysp_module * (module used for resolving prefixes from imports) * LY_VALUE_SCHEMA_RESOLVED - struct lyd_value_prefix * (sized array of pairs: prefix - module) * LY_VALUE_XML - const struct ly_set * (set with defined namespaces stored as ::lyxml_ns) + * LY_VALUE_CBOR - NULL * LY_VALUE_JSON - NULL * LY_VALUE_LYB - NULL * @return Resolved prefix module, diff --git a/src/xpath.c b/src/xpath.c index df078772e..42989654a 100644 --- a/src/xpath.c +++ b/src/xpath.c @@ -5752,6 +5752,7 @@ moveto_resolve_module(const char **qname, uint32_t *qname_len, const struct lyxp mod = set->cur_mod; break; case LY_VALUE_CANON: + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: @@ -6417,6 +6418,7 @@ moveto_scnode_check(const struct lysc_node *node, const struct lysc_node *ctx_sc /* use current module */ moveto_mod = set->cur_mod; break; + case LY_VALUE_CBOR: case LY_VALUE_JSON: case LY_VALUE_LYB: case LY_VALUE_STR_NS: From 93d4684c7c0e57e0c2c7f9ce7b505d315b7053bb Mon Sep 17 00:00:00 2001 From: MeherRushi Date: Mon, 3 Nov 2025 06:12:44 +0000 Subject: [PATCH 6/8] feat: implement CBOR printer functionality --- src/printer_cbor.c | 1765 +++++++++++++++++++++++++++++--------------- 1 file changed, 1167 insertions(+), 598 deletions(-) diff --git a/src/printer_cbor.c b/src/printer_cbor.c index d0cc0997a..dc4fc2a65 100644 --- a/src/printer_cbor.c +++ b/src/printer_cbor.c @@ -1,9 +1,9 @@ /** * @file printer_cbor.c - * @author Meher Rushi - * @brief CBOR printer for libyang data tree using libcbor + * @author Meher Rushi + * @brief CBOR printer for libyang data structure * - * Copyright (c) 2024 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2023 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. @@ -12,771 +12,1340 @@ * https://opensource.org/licenses/BSD-3-Clause */ -#include "printer_data.h" +#define _GNU_SOURCE + +#ifdef ENABLE_CBOR_SUPPORT #include -#include -#include #include #include #include -#include - #include "context.h" #include "log.h" #include "ly_common.h" #include "out.h" -#include "plugins_exts.h" +#include "out_internal.h" +#include "parser_data.h" +#include "plugins_exts/metadata.h" +#include "plugins_types.h" +#include "printer_data.h" #include "printer_internal.h" #include "set.h" +#include "tree.h" #include "tree_data.h" #include "tree_schema.h" +#include "cbor.h" /** - * @brief CBOR printer context + * @brief CBOR printer context. */ struct cborpr_ctx { - struct ly_out *out; /**< output structure */ - const struct lyd_node *root; /**< root node of the subtree being printed */ - const struct lyd_node *print_sibling_metadata; /**< node with metadata supposed to be printed */ - ly_bool simple_status; /**< flag for simple status */ + struct ly_out *out; /**< output specification */ + const struct lyd_node *root; /**< root node of the subtree being printed */ + const struct lyd_node *parent; /**< parent of the node being printed */ + uint32_t options; /**< [Data printer flags](@ref dataprinterflags) */ + const struct ly_ctx *ctx; /**< libyang context */ - uint16_t level; /**< current nesting level */ - uint32_t options; /**< [printer flags](@ref dataprinterflags) */ - const struct ly_ctx *ctx; /**< libyang context */ - - struct ly_set prefix; /**< printed module prefixes */ - uint32_t array_index; /**< index in array if we are printing an array element */ + struct ly_set open; /**< currently open array(s) */ + const struct lyd_node *first_leaflist; /**< first printed leaf-list instance, used when printing its metadata/attributes */ - cbor_item_t *root_item; /**< root CBOR item */ + cbor_item_t *root_map; /**< root CBOR map */ }; +static LY_ERR cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map); + /** - * @brief Check if module needs prefix - ROBUST VERSION + * @brief Compare 2 nodes, despite it is regular data node or an opaq node, and + * decide if they corresponds to the same schema node. + * + * @return 1 - matching nodes, 0 - non-matching nodes */ -static ly_bool -cbor_module_needs_prefix(struct cborpr_ctx *ctx, const struct lys_module *module) +static int +matching_node(const struct lyd_node *node1, const struct lyd_node *node2) { - /* CRITICAL: Add comprehensive null checks */ - if (!ctx) { - fprintf(stderr, "DEBUG: cbor_module_needs_prefix called with NULL ctx\n"); + assert(node1 || node2); + + if (!node1 || !node2) { return 0; - } - - if (!module) { - fprintf(stderr, "DEBUG: cbor_module_needs_prefix called with NULL module\n"); + } else if (node1->schema != node2->schema) { return 0; } - - if (!module->name) { - fprintf(stderr, "DEBUG: Module has NULL name\n"); - return 0; + if (!node1->schema) { + /* compare node names */ + struct lyd_node_opaq *onode1 = (struct lyd_node_opaq *)node1; + struct lyd_node_opaq *onode2 = (struct lyd_node_opaq *)node2; + + if ((onode1->name.name != onode2->name.name) || (onode1->name.prefix != onode2->name.prefix)) { + return 0; + } } - - fprintf(stderr, "DEBUG: Checking prefix for module: '%s'\n", module->name); - - /* Always add prefix if explicitly requested */ - if (ctx->options & LYD_PRINT_WD_ALL_TAG) { - fprintf(stderr, "DEBUG: Prefix requested via options\n"); + + return 1; +} + +/** + * @brief Open a CBOR array for the specified @p node + * + * @param[in] pctx CBOR printer context. + * @param[in] node First node of the array. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_array_open(struct cborpr_ctx *pctx, const struct lyd_node *node) +{ + LY_CHECK_RET(ly_set_add(&pctx->open, (void *)node, 0, NULL)); + return LY_SUCCESS; +} + +/** + * @brief Get know if the array for the provided @p node is currently open. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to check. + * @return 1 in case the printer is currently in the array belonging to the provided @p node. + * @return 0 in case the provided @p node is not connected with the currently open array (or there is no open array). + */ +static int +is_open_array(struct cborpr_ctx *pctx, const struct lyd_node *node) +{ + if (pctx->open.count && matching_node(node, pctx->open.dnodes[pctx->open.count - 1])) { return 1; - } - - /* Check if it's an internal libyang module */ - if (!strcmp(module->name, "ietf-yang-metadata") || - !strcmp(module->name, "yang") || - !strcmp(module->name, "ietf-inet-types") || - !strcmp(module->name, "ietf-yang-types") || - !strcmp(module->name, "ietf-yang-structure-ext")) { - fprintf(stderr, "DEBUG: Internal module, no prefix needed\n"); + } else { return 0; } - - /* For now, don't add prefixes unless explicitly requested */ - fprintf(stderr, "DEBUG: No prefix needed for module: '%s'\n", module->name); - return 0; } +/** + * @brief Close the most inner CBOR array. + * + * @param[in] pctx CBOR printer context. + */ +static void +cbor_print_array_close(struct cborpr_ctx *pctx) +{ + ly_set_rm_index(&pctx->open, pctx->open.count - 1, NULL); +} /** - * @brief Safe wrapper for cbor_build_string that handles NULL inputs - ENHANCED + * @brief Get the node's module name to use as the @p node prefix in CBOR. + * + * @param[in] node Node to process. + * @return The name of the module where the @p node belongs, it can be NULL in case the module name + * cannot be determined (source format is XML and the refered namespace is unknown/not implemented in the current context). */ -static cbor_item_t * -safe_cbor_build_string(const char *str) +static const char * +node_prefix(const struct lyd_node *node) { - if (!str) { - fprintf(stderr, "DEBUG: NULL string passed to safe_cbor_build_string, using empty string\n"); - return cbor_build_string(""); - } - - fprintf(stderr, "DEBUG: Building CBOR string: '%s' (len=%zu)\n", str, strlen(str)); - cbor_item_t *item = cbor_build_string(str); - if (!item) { - fprintf(stderr, "DEBUG: cbor_build_string failed for '%s'\n", str); + if (node->schema) { + return node->schema->module->name; + } else { + struct lyd_node_opaq *onode = (struct lyd_node_opaq *)node; + const struct lys_module *mod; + + switch (onode->format) { + case LY_VALUE_CBOR: + case LY_VALUE_JSON: + return onode->name.module_name; + case LY_VALUE_XML: + mod = ly_ctx_get_module_implemented_ns(onode->ctx, onode->name.module_ns); + if (!mod) { + return NULL; + } + return mod->name; + default: + /* cannot be created */ + LOGINT(LYD_CTX(node)); + } } - return item; + + return NULL; } /** - * @brief Convert YANG value to CBOR item + * @brief Compare 2 nodes if the belongs to the same module (if they come from the same namespace) + * + * Accepts both regulard a well as opaq nodes. + * + * @param[in] node1 The first node to compare. + * @param[in] node2 The second node to compare. + * @return 0 in case the nodes' modules are the same + * @return 1 in case the nodes belongs to different modules */ -static cbor_item_t * -cbor_value_to_item(const struct lyd_node *node) +int +cbor_nscmp(const struct lyd_node *node1, const struct lyd_node *node2) { - const char *str; - cbor_item_t *item = NULL; - - if (!node) { - fprintf(stderr, "DEBUG: cbor_value_to_item called with NULL node\n"); - return safe_cbor_build_string(""); - } - - fprintf(stderr, "DEBUG: cbor_value_to_item - node type: %d\n", node->schema->nodetype); - - if (!(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST))) { - fprintf(stderr, "DEBUG: Non-leaf node, returning empty string\n"); - return safe_cbor_build_string(""); /* Empty string for non-leaf nodes */ + assert(node1 || node2); + + if (!node1 || !node2) { + return 1; + } else if (node1->schema && node2->schema) { + if (node1->schema->module == node2->schema->module) { + /* belongs to the same module */ + return 0; + } else { + /* different modules */ + return 1; + } + } else { + const char *pref1 = node_prefix(node1); + const char *pref2 = node_prefix(node2); + + if ((pref1 && pref2) && (pref1 == pref2)) { + return 0; + } else { + return 1; + } } +} + +/** + * @brief Create CBOR member name as [prefix:]name + * + * @param[in] pctx CBOR printer context. + * @param[in] node The data node being printed. + * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier. + * @return Newly allocated string with the member name, NULL on error. + */ +static char * +cbor_print_member_name(struct cborpr_ctx *pctx, const struct lyd_node *node, ly_bool is_attr) +{ + char *name = NULL; + const char *prefix_str = node_prefix(node); + const char *node_name = node->schema->name; - str = lyd_get_value(node); - fprintf(stderr, "DEBUG: Node value: '%s'\n", str ? str : "NULL"); - - if (!str || strlen(str) == 0) { - fprintf(stderr, "DEBUG: Empty or NULL value, returning empty string\n"); - return safe_cbor_build_string(""); + if (cbor_nscmp(node, pctx->parent)) { + /* print "namespace" */ + if (is_attr) { + if (asprintf(&name, "@%s:%s", prefix_str, node_name) == -1) { + return NULL; + } + } else { + if (asprintf(&name, "%s:%s", prefix_str, node_name) == -1) { + return NULL; + } + } + } else { + if (is_attr) { + if (asprintf(&name, "@%s", node_name) == -1) { + return NULL; + } + } else { + name = strdup(node_name); + } } - /* FIXED: Add null check for schema before casting */ - if (!node->schema) { - fprintf(stderr, "DEBUG: Node has NULL schema, using string value\n"); - return cbor_build_string(str); + return name; +} + +/** + * @brief More generic alternative to cbor_print_member_name() to print some special cases of the member names. + * + * @param[in] pctx CBOR printer context. + * @param[in] parent Parent node to compare modules deciding if the prefix is printed. + * @param[in] format Format to decide how to process the @p prefix. + * @param[in] name Name structure to provide name and prefix to print. If NULL, only "" name is printed. + * @param[in] is_attr Flag if the metadata sign (@) is supposed to be added before the identifier. + * @return Newly allocated string with the member name, NULL on error. + */ +static char * +cbor_print_member_name2(struct cborpr_ctx *pctx, const struct lyd_node *parent, LY_VALUE_FORMAT format, + const struct ly_opaq_name *name, ly_bool is_attr) +{ + const char *module_name = NULL, *name_str; + char *result = NULL; + + /* determine prefix string */ + if (name) { + switch (format) { + case LY_VALUE_CBOR: + case LY_VALUE_JSON: + module_name = name->module_name; + break; + case LY_VALUE_XML: { + const struct lys_module *mod = NULL; + + if (name->module_ns) { + mod = ly_ctx_get_module_implemented_ns(pctx->ctx, name->module_ns); + } + if (mod) { + module_name = mod->name; + } + break; + } + default: + /* cannot be created */ + LOGINT_RET(pctx->ctx); + } + + name_str = name->name; + } else { + name_str = ""; } - - /* Handle different data types based on YANG type */ - switch (((struct lysc_node_leaf *)node->schema)->type->basetype) { - case LY_TYPE_BOOL: - if (strcmp(str, "true") == 0) { - item = cbor_build_bool(true); + + /* create the member name */ + if (module_name && (!parent || (node_prefix(parent) != module_name))) { + if (is_attr) { + if (asprintf(&result, "@%s:%s", module_name, name_str) == -1) { + return NULL; + } } else { - item = cbor_build_bool(false); + if (asprintf(&result, "%s:%s", module_name, name_str) == -1) { + return NULL; + } + } + } else { + if (is_attr) { + if (asprintf(&result, "@%s", name_str) == -1) { + return NULL; + } + } else { + result = strdup(name_str); } + } + + return result; +} + +/** + * @brief Print data value to CBOR item. + * + * @param[in] pctx CBOR printer context. + * @param[in] ctx Context used to print the value. + * @param[in] val Data value to be printed. + * @param[in] local_mod Module of the current node. + * @param[out] item_p Pointer to store the created CBOR item. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_value(struct cborpr_ctx *pctx, const struct ly_ctx *ctx, const struct lyd_value *val, + const struct lys_module *local_mod, cbor_item_t **item_p) +{ + ly_bool dynamic; + LY_DATA_TYPE basetype; + const char *value; + cbor_item_t *item = NULL; + + value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, (void *)local_mod, &dynamic, NULL); + LY_CHECK_RET(!value, LY_EINVAL); + basetype = val->realtype->basetype; + +print_val: + /* leafref is not supported */ + switch (basetype) { + case LY_TYPE_UNION: + /* use the resolved type */ + val = &val->subvalue->value; + basetype = val->realtype->basetype; + goto print_val; + + case LY_TYPE_BINARY: + case LY_TYPE_STRING: + case LY_TYPE_BITS: + case LY_TYPE_ENUM: + case LY_TYPE_INST: + case LY_TYPE_IDENT: + /* string types */ + item = cbor_build_string(value); break; - + + case LY_TYPE_INT64: + case LY_TYPE_UINT64: + case LY_TYPE_DEC64: { + /* numeric types stored as strings in CBOR */ + item = cbor_build_string(value); + break; + } + case LY_TYPE_INT8: case LY_TYPE_INT16: - case LY_TYPE_INT32: - case LY_TYPE_INT64: { - char *endptr; - long long num = strtoll(str, &endptr, 10); - if (*endptr == '\0') { - if (num >= 0) { - item = cbor_build_uint64((uint64_t)num); - } else { - item = cbor_build_negint64((uint64_t)(-num - 1)); - } + case LY_TYPE_INT32: { + /* signed integer types */ + int64_t num = strtoll(value, NULL, 10); + if (num >= 0) { + item = cbor_build_uint64(num); } else { - item = cbor_build_string(str); + item = cbor_build_negint64(-num - 1); } break; } - + case LY_TYPE_UINT8: case LY_TYPE_UINT16: - case LY_TYPE_UINT32: - case LY_TYPE_UINT64: { - char *endptr; - unsigned long long num = strtoull(str, &endptr, 10); - if (*endptr == '\0') { - item = cbor_build_uint64(num); - } else { - item = cbor_build_string(str); - } + case LY_TYPE_UINT32: { + /* unsigned integer types */ + uint64_t num = strtoull(value, NULL, 10); + item = cbor_build_uint64(num); break; } - - case LY_TYPE_DEC64: { - char *endptr; - double num = strtod(str, &endptr); - if (*endptr == '\0') { - item = cbor_build_float8(num); + + case LY_TYPE_BOOL: + /* boolean */ + if (strcmp(value, "true") == 0) { + item = cbor_build_bool(true); } else { - item = cbor_build_string(str); + item = cbor_build_bool(false); } break; - } - + case LY_TYPE_EMPTY: - item = cbor_build_string(""); + /* empty type is represented as [null] */ + item = cbor_new_definite_array(1); + if (item) { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { + cbor_array_push(item, null_item); + cbor_decref(&null_item); + } + } break; - + default: - /* String types and others */ - item = cbor_build_string(str); - break; + /* error */ + LOGINT_RET(pctx->ctx); } - - return item; + + if (dynamic) { + free((char *)value); + } + + *item_p = item; + return item ? LY_SUCCESS : LY_EMEM; } /** - * @brief Count direct children of a node + * @brief Print all the attributes of the opaq node to CBOR map. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Opaq node where the attributes are placed. + * @param[in] attr_map CBOR map to add attributes to. + * @return LY_ERR value. */ -static size_t -cbor_count_children(const struct lyd_node *node) +static LY_ERR +cbor_print_attribute(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_item_t *attr_map) { - size_t count = 0; - const struct lyd_node *child; - - LY_LIST_FOR(lyd_child(node), child) { - count++; + struct lyd_attr *attr; + cbor_item_t *value_item = NULL; + + for (attr = node->attr; attr; attr = attr->next) { + char *key = cbor_print_member_name2(pctx, &node->node, attr->format, &attr->name, 0); + LY_CHECK_RET(!key, LY_EMEM); + + if (attr->hints & (LYD_VALHINT_STRING | LYD_VALHINT_OCTNUM | LYD_VALHINT_HEXNUM | LYD_VALHINT_NUM64)) { + value_item = cbor_build_string(attr->value); + } else if (attr->hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) { + if (strcmp(attr->value, "true") == 0) { + value_item = cbor_build_bool(true); + } else if (strcmp(attr->value, "false") == 0) { + value_item = cbor_build_bool(false); + } else { + /* numeric value as string */ + value_item = cbor_build_string(attr->value); + } + } else if (attr->hints & LYD_VALHINT_EMPTY) { + value_item = cbor_new_definite_array(1); + if (value_item) { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { + cbor_array_push(value_item, null_item); + cbor_decref(&null_item); + } + } + } else { + /* unknown value format with no hints, use universal string */ + value_item = cbor_build_string(attr->value); + } + + if (!value_item) { + free(key); + return LY_EMEM; + } + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + + if (!cbor_map_add(attr_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } } - return count; + + return LY_SUCCESS; } /** - * @brief Count sibling nodes with the same name (for leaf-lists) + * @brief Print all the metadata of the node to CBOR map. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Node where the metadata are placed. + * @param[in] wdmod With-defaults module to mark that default attribute is supposed to be printed. + * @param[in] meta_map CBOR map to add metadata to. + * @return LY_ERR value. */ -static size_t -cbor_count_siblings_same_name(const struct lyd_node *node) +static LY_ERR +cbor_print_metadata(struct cborpr_ctx *pctx, const struct lyd_node *node, const struct lys_module *wdmod, + cbor_item_t *meta_map) { - size_t count = 0; - const struct lyd_node *sibling; - const struct lysc_node *schema = node->schema; - - /* Check if this is a leaf-list */ - if (!(schema->nodetype & LYS_LEAFLIST)) { - return 1; + struct lyd_meta *meta; + cbor_item_t *value_item = NULL; + char *key = NULL; + + if (wdmod) { + if (asprintf(&key, "%s:default", wdmod->name) == -1) { + return LY_EMEM; + } + value_item = cbor_build_bool(true); + if (!value_item) { + free(key); + return LY_EMEM; + } + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + + if (!cbor_map_add(meta_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } } - - /* Count siblings with same schema */ - LY_LIST_FOR(node, sibling) { - if (sibling->schema == schema) { - count++; - } else { - break; /* leaf-list instances are consecutive */ + + for (meta = node->meta; meta; meta = meta->next) { + if (!lyd_metadata_should_print(meta)) { + continue; + } + + if (asprintf(&key, "%s:%s", meta->annotation->module->name, meta->name) == -1) { + return LY_EMEM; + } + + LY_CHECK_RET(cbor_print_value(pctx, LYD_CTX(node), &meta->value, NULL, &value_item)); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + + if (!cbor_map_add(meta_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; } } - - return count; + + return LY_SUCCESS; } /** - * @brief Print a single node recursively + * @brief Check if a value can be printed for at least one metadata. + * + * @param[in] node Node to check. + * @return 1 if node has printable meta otherwise 0. */ -static LY_ERR cbor_print_node(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map); +static ly_bool +node_has_printable_meta(const struct lyd_node *node) +{ + struct lyd_meta *iter; + + if (!node->meta) { + return 0; + } + + LY_LIST_FOR(node->meta, iter) { + if (lyd_metadata_should_print(iter)) { + return 1; + } + } + + return 0; +} /** - * @brief Print container or list node - EXTRA SAFE VERSION + * @brief Print attributes/metadata of the given @p node to CBOR map. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node where the attributes/metadata are placed. + * @param[in] parent_map CBOR map to add the metadata to. + * @param[in] inner Flag if the @p node is an inner node in the tree. + * @return LY_ERR value. */ static LY_ERR -cbor_print_container(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +cbor_print_attributes(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map, ly_bool inner) { - cbor_item_t *node_map = NULL; - cbor_item_t *key_item = NULL; - char *node_name = NULL; - const struct lyd_node *child; - LY_ERR ret = LY_SUCCESS; - size_t child_count; - - fprintf(stderr, "DEBUG: cbor_print_container called for node: %s\n", - node && node->schema && node->schema->name ? node->schema->name : "NULL"); - - /* COMPREHENSIVE NULL CHECKS */ - if (!ctx) { - fprintf(stderr, "DEBUG: Container called with NULL ctx\n"); - return LY_EINVAL; - } - - if (!node) { - fprintf(stderr, "DEBUG: Container called with NULL node\n"); - return LY_EINVAL; - } - - if (!node->schema) { - fprintf(stderr, "DEBUG: Container node has NULL schema\n"); - return LY_EINVAL; - } - - if (!parent_map) { - fprintf(stderr, "DEBUG: Container called with NULL parent_map\n"); - return LY_EINVAL; - } - - /* Get node name - This should now be safe */ - // node_name = cbor_get_node_name(ctx, node); - node_name = ""; if (!node_name) { - fprintf(stderr, "DEBUG: Failed to get container node name\n"); - ret = LY_EMEM; - goto cleanup; - } - - fprintf(stderr, "DEBUG: Container name: '%s'\n", node_name); - - /* Count children */ - child_count = cbor_count_children(node); - fprintf(stderr, "DEBUG: Container has %zu children\n", child_count); - - /* Create map for this container/list */ - node_map = cbor_new_definite_map(child_count); - if (!node_map) { - fprintf(stderr, "DEBUG: Failed to create CBOR map for container\n"); - ret = LY_EMEM; - goto cleanup; + const struct lys_module *wdmod = NULL; + cbor_item_t *meta_map = NULL; + char *key = NULL; + + if (node->schema && (node->schema->nodetype != LYS_CONTAINER) && (((node->flags & LYD_DEFAULT) && + (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) || + ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node)))) { + /* we have implicit OR explicit default node */ + /* get with-defaults module */ + wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults"); } - - /* Add all children to the map */ - LY_LIST_FOR(lyd_child(node), child) { - if (!child || !child->schema || !child->schema->name) { - fprintf(stderr, "DEBUG: Skipping invalid child\n"); - continue; + + if (node->schema && (wdmod || node_has_printable_meta(node))) { + meta_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!meta_map, LY_EMEM); + + LY_CHECK_RET(cbor_print_metadata(pctx, node, wdmod, meta_map)); + + if (inner) { + key = cbor_print_member_name2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1); + } else { + key = cbor_print_member_name(pctx, node, 1); } - - fprintf(stderr, "DEBUG: Processing child: %s\n", child->schema->name); - ret = cbor_print_node(ctx, child, node_map); - if (ret != LY_SUCCESS) { - fprintf(stderr, "DEBUG: Failed to process child node\n"); - goto cleanup; + LY_CHECK_RET(!key, LY_EMEM); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(meta_map) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; } - } - - /* Add this container/list to parent map */ - key_item = safe_cbor_build_string(node_name); - if (!key_item) { - fprintf(stderr, "DEBUG: Failed to create key item for container\n"); - ret = LY_EMEM; - goto cleanup; - } - - if (!cbor_map_add(parent_map, (struct cbor_pair) { - .key = key_item, - .value = node_map - })) { - fprintf(stderr, "DEBUG: Failed to add container to parent map\n"); - ret = LY_EMEM; - goto cleanup; - } - - fprintf(stderr, "DEBUG: Container added successfully\n"); - - /* Items are now owned by the map, don't decref them */ - key_item = NULL; - node_map = NULL; + } else if (!node->schema && ((struct lyd_node_opaq *)node)->attr) { + meta_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!meta_map, LY_EMEM); -cleanup: - if (key_item) { - cbor_decref(&key_item); - } - if (node_map) { - cbor_decref(&node_map); - } - if (node_name) { - free(node_name); + LY_CHECK_RET(cbor_print_attribute(pctx, (struct lyd_node_opaq *)node, meta_map)); + + if (inner) { + key = cbor_print_member_name2(pctx, lyd_parent(node), LY_VALUE_JSON, NULL, 1); + } else { + key = cbor_print_member_name2(pctx, lyd_parent(node), ((struct lyd_node_opaq *)node)->format, + &((struct lyd_node_opaq *)node)->name, 1); + } + LY_CHECK_RET(!key, LY_EMEM); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(meta_map) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } } - return ret; + + return LY_SUCCESS; } /** - * @brief Print leaf or leaf-list node + * @brief Print leaf data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the leaf to. + * @return LY_ERR value. */ static LY_ERR -cbor_print_leaf(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +cbor_print_leaf(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) { - cbor_item_t *key_item = NULL; + char *key = NULL; cbor_item_t *value_item = NULL; - cbor_item_t *array_item = NULL; - char *node_name = NULL; - const struct lyd_node *sibling; - LY_ERR ret = LY_SUCCESS; - size_t sibling_count; - - fprintf(stderr, "DEBUG: cbor_print_leaf called for node: %s\n", - node && node->schema && node->schema->name ? node->schema->name : "NULL"); + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_RET(!key, LY_EMEM); + + LY_CHECK_ERR_RET(cbor_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, + node->schema->module, &value_item), free(key), LY_EINVAL); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; - /* FIXED: Add null checks */ - if (!node || !node->schema) { - fprintf(stderr, "DEBUG: Leaf node or schema is NULL\n"); - return LY_EINVAL; + if (!cbor_map_add(parent_map, pair)) { + free(key); + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; } - - /* Get node name */ - // node_name = cbor_get_node_name(ctx, node); - node_name = ""; - if (!node_name) { - fprintf(stderr, "DEBUG: Failed to get node name\n"); - ret = LY_EMEM; - goto cleanup; - } - - fprintf(stderr, "DEBUG: Got node name: '%s'\n", node_name); - - /* Check if this is a leaf-list with multiple values */ - sibling_count = cbor_count_siblings_same_name(node); - fprintf(stderr, "DEBUG: Sibling count: %zu\n", sibling_count); - - if (sibling_count > 1 && (node->schema->nodetype & LYS_LEAFLIST)) { - fprintf(stderr, "DEBUG: Processing leaf-list with %zu values\n", sibling_count); - - /* Create array for leaf-list */ - array_item = cbor_new_definite_array(sibling_count); - if (!array_item) { - fprintf(stderr, "DEBUG: Failed to create CBOR array\n"); - ret = LY_EMEM; - goto cleanup; - } - - /* Add all values to array */ - LY_LIST_FOR(node, sibling) { - if (sibling->schema != node->schema) { - break; /* Different schema, stop */ - } - - fprintf(stderr, "DEBUG: Adding leaf-list value to array\n"); - value_item = cbor_value_to_item(sibling); - if (!value_item) { - fprintf(stderr, "DEBUG: Failed to create CBOR value item\n"); - ret = LY_EMEM; - goto cleanup; - } - - if (!cbor_array_push(array_item, value_item)) { - fprintf(stderr, "DEBUG: Failed to add item to CBOR array\n"); - cbor_decref(&value_item); - ret = LY_EMEM; - goto cleanup; - } - - value_item = NULL; /* Array owns it now */ - } - - /* Add array to parent map */ - key_item = safe_cbor_build_string(node_name); - if (!key_item) { - fprintf(stderr, "DEBUG: Failed to create CBOR key item for leaf-list\n"); - ret = LY_EMEM; - goto cleanup; - } - - if (!cbor_map_add(parent_map, (struct cbor_pair) { - .key = key_item, - .value = array_item - })) { - fprintf(stderr, "DEBUG: Failed to add leaf-list to parent map\n"); - ret = LY_EMEM; - goto cleanup; - } - - /* Items are now owned by the map */ - key_item = NULL; - array_item = NULL; - - } else { - fprintf(stderr, "DEBUG: Processing single leaf value\n"); - - /* Single leaf value */ - value_item = cbor_value_to_item(node); - if (!value_item) { - fprintf(stderr, "DEBUG: Failed to create CBOR value item for leaf\n"); - ret = LY_EMEM; - goto cleanup; - } - - key_item = safe_cbor_build_string(node_name); - if (!key_item) { - fprintf(stderr, "DEBUG: Failed to create CBOR key item for leaf\n"); - ret = LY_EMEM; - goto cleanup; - } - - if (!cbor_map_add(parent_map, (struct cbor_pair) { - .key = key_item, - .value = value_item - })) { - fprintf(stderr, "DEBUG: Failed to add leaf to parent map\n"); - ret = LY_EMEM; - goto cleanup; - } - - /* Items are now owned by the map */ - key_item = NULL; - value_item = NULL; - } - - fprintf(stderr, "DEBUG: cbor_print_leaf completed successfully\n"); + free(key); -cleanup: - if (key_item) { - cbor_decref(&key_item); - } - if (value_item) { - cbor_decref(&value_item); - } - if (array_item) { - cbor_decref(&array_item); - } - free(node_name); - return ret; + /* print attributes as sibling */ + cbor_print_attributes(pctx, node, parent_map, 0); + + return LY_SUCCESS; } /** - * @brief Print anydata/anyxml node + * @brief Print anydata/anyxml content to CBOR item. + * + * @param[in] pctx CBOR printer context. + * @param[in] any Anydata node to print. + * @param[out] item_p Pointer to store the created CBOR item. + * @return LY_ERR value. */ static LY_ERR -cbor_print_any(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +cbor_print_any_content(struct cborpr_ctx *pctx, struct lyd_node_any *any, cbor_item_t **item_p) { - cbor_item_t *key_item = NULL; - cbor_item_t *value_item = NULL; - char *node_name = NULL; LY_ERR ret = LY_SUCCESS; - struct lyd_node_any *any = (struct lyd_node_any *)node; - const char *value_str = ""; - - /* FIXED: Add null checks */ - if (!node || !node->schema) { - fprintf(stderr, "DEBUG: Any node or schema is NULL\n"); - return LY_EINVAL; + struct lyd_node *iter; + const struct lyd_node *prev_parent; + uint32_t prev_opts, *prev_lo, temp_lo = 0; + cbor_item_t *content_map = NULL; + + assert(any->schema->nodetype & LYD_NODE_ANY); + + if ((any->schema->nodetype == LYS_ANYDATA) && (any->value_type != LYD_ANYDATA_DATATREE)) { + LOGINT_RET(pctx->ctx); } - - /* Get node name */ - // node_name = cbor_get_node_name(ctx, node); - node_name = ""; if (!node_name) { - ret = LY_EMEM; - goto cleanup; + if (any->value_type == LYD_ANYDATA_LYB) { + uint32_t parser_options = LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT; + + /* turn logging off */ + prev_lo = ly_temp_log_options(&temp_lo); + + /* try to parse it into a data tree */ + if (lyd_parse_data_mem(pctx->ctx, any->value.mem, LYD_LYB, parser_options, 0, &iter) == LY_SUCCESS) { + /* successfully parsed */ + free(any->value.mem); + any->value.tree = iter; + any->value_type = LYD_ANYDATA_DATATREE; + } + + /* turn logging on again */ + ly_temp_log_options(prev_lo); } - - /* Convert anydata to string representation for now */ - /* TODO: Could be enhanced to preserve the actual data format */ + switch (any->value_type) { - case LYD_ANYDATA_STRING: - value_str = any->value.str ? (char *)any->value.str : ""; - break; case LYD_ANYDATA_DATATREE: - /* For now, just indicate it's a data tree */ - value_str = "[DATA TREE]"; - break; - case LYD_ANYDATA_XML: - value_str = any->value.str ? (char *)any->value.str : ""; + /* create a map for the content */ + content_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!content_map, LY_EMEM); + + /* print data tree */ + prev_parent = pctx->parent; + prev_opts = pctx->options; + pctx->parent = &any->node; + pctx->options &= ~LYD_PRINT_WITHSIBLINGS; + LY_LIST_FOR(any->value.tree, iter) { + ret = cbor_print_node(pctx, iter, content_map); + LY_CHECK_ERR_RET(ret, cbor_decref(&content_map), ret); + } + pctx->parent = prev_parent; + pctx->options = prev_opts; + + *item_p = content_map; break; case LYD_ANYDATA_JSON: - value_str = any->value.str ? (char *)any->value.str : ""; + if (!any->value.json) { + /* no content */ + if (any->schema->nodetype == LYS_ANYXML) { + *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); + } else { + *item_p = cbor_new_indefinite_map(); + } + } else { + /* JSON content - store as string */ + *item_p = cbor_build_string(any->value.json); + } break; - default: - value_str = ""; + case LYD_ANYDATA_STRING: + case LYD_ANYDATA_XML: + if (!any->value.str) { + /* no content */ + if (any->schema->nodetype == LYS_ANYXML) { + *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); + } else { + *item_p = cbor_new_indefinite_map(); + } + } else { + /* print as a string */ + *item_p = cbor_build_string(any->value.str); + } + break; + case LYD_ANYDATA_LYB: + /* LYB format is not supported */ + LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as CBOR.", any->value_type); + *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); break; } - - value_item = cbor_build_string(value_str); - if (!value_item) { - ret = LY_EMEM; - goto cleanup; + + return LY_SUCCESS; +} + +/** + * @brief Print content of a single container/list data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[out] item_p Pointer to store the created CBOR map. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_inner(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t **item_p) +{ + struct lyd_node *child; + const struct lyd_node *prev_parent; + cbor_item_t *inner_map = NULL; + + /* create map for inner node */ + inner_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!inner_map, LY_EMEM); + + /* print attributes first */ + cbor_print_attributes(pctx, node, inner_map, 1); + + /* print children */ + prev_parent = pctx->parent; + pctx->parent = node; + LY_LIST_FOR(lyd_child(node), child) { + LY_CHECK_ERR_RET(cbor_print_node(pctx, child, inner_map), cbor_decref(&inner_map), LY_EINVAL); } + pctx->parent = prev_parent; + + *item_p = inner_map; + return LY_SUCCESS; +} + +/** + * @brief Print container data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the container to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_container(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + char *key = NULL; + cbor_item_t *inner_map = NULL; + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_RET(!key, LY_EMEM); + + LY_CHECK_ERR_RET(cbor_print_inner(pctx, node, &inner_map), free(key), LY_EINVAL); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(inner_map) + }; - key_item = cbor_build_string(node_name); - if (!key_item) { - ret = LY_EMEM; - goto cleanup; + if (!cbor_map_add(parent_map, pair)) { + free(key); + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; } + free(key); + + return LY_SUCCESS; +} + +/** + * @brief Print anydata/anyxml data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the anydata to. + * @return LY_ERR value. + */ +static LY_ERR +cbor_print_any(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) +{ + char *key = NULL; + cbor_item_t *any_item = NULL; + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_RET(!key, LY_EMEM); + + LY_CHECK_ERR_RET(cbor_print_any_content(pctx, (struct lyd_node_any *)node, &any_item), free(key), LY_EINVAL); + + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(any_item) + }; - if (!cbor_map_add(parent_map, (struct cbor_pair) { - .key = key_item, - .value = value_item - })) { - ret = LY_EMEM; - goto cleanup; + if (!cbor_map_add(parent_map, pair)) { + free(key); + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + free(key); + + /* print attributes as sibling */ + cbor_print_attributes(pctx, node, parent_map, 0); + + return LY_SUCCESS; +} + +/** + * @brief Check whether a node is the last printed instance of a (leaf-)list. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Last printed node. + * @return Whether it is the last printed instance or not. + */ +static ly_bool +cbor_print_array_is_last_inst(struct cborpr_ctx *pctx, const struct lyd_node *node) +{ + if (!is_open_array(pctx, node)) { + /* no array open */ + return 0; } - - /* Items are now owned by the map */ - key_item = NULL; - value_item = NULL; -cleanup: - if (key_item) { - cbor_decref(&key_item); + if ((pctx->root == node) && !(pctx->options & LYD_PRINT_WITHSIBLINGS)) { + /* the only printed instance */ + return 1; } - if (value_item) { - cbor_decref(&value_item); + + if (!node->next || (node->next->schema != node->schema)) { + /* last instance */ + return 1; } - free(node_name); - return ret; + + return 0; } /** - * @brief Print a single node recursively + * @brief Print single leaf-list or list instance. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the node to. + * @param[in,out] array_p Pointer to the array being built. + * @return LY_ERR value. */ static LY_ERR -cbor_print_node(struct cborpr_ctx *ctx, const struct lyd_node *node, cbor_item_t *parent_map) +cbor_print_leaf_list(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map, cbor_item_t **array_p) { - /* FIXED: Add null checks at the beginning */ - if (!node || !node->schema) { - fprintf(stderr, "DEBUG: cbor_print_node called with NULL node or schema\n"); - return LY_EINVAL; + const struct lys_module *wdmod = NULL; + cbor_item_t *value_item = NULL; + cbor_item_t *inner_map = NULL; + char *key = NULL; + + if (!is_open_array(pctx, node)) { + /* start new array */ + *array_p = cbor_new_indefinite_array(); + LY_CHECK_RET(!*array_p, LY_EMEM); + + key = cbor_print_member_name(pctx, node, 0); + LY_CHECK_ERR_RET(!key, cbor_decref(array_p), LY_EMEM); + + LY_CHECK_RET(cbor_print_array_open(pctx, node)); } - - switch (node->schema->nodetype) { - case LYS_CONTAINER: - case LYS_LIST: - return cbor_print_container(ctx, node, parent_map); - case LYS_LEAF: - case LYS_LEAFLIST: - return cbor_print_leaf(ctx, node, parent_map); - case LYS_ANYXML: - case LYS_ANYDATA: - return cbor_print_any(ctx, node, parent_map); - default: - /* Skip unknown node types */ - fprintf(stderr, "DEBUG: Skipping unknown node type: %d\n", node->schema->nodetype); - return LY_SUCCESS; + + if (node->schema->nodetype == LYS_LIST) { + /* print list's content */ + LY_CHECK_RET(cbor_print_inner(pctx, node, &inner_map)); + cbor_array_push(*array_p, inner_map); + cbor_decref(&inner_map); + } else { + assert(node->schema->nodetype == LYS_LEAFLIST); + + LY_CHECK_RET(cbor_print_value(pctx, LYD_CTX(node), &((const struct lyd_node_term *)node)->value, + node->schema->module, &value_item)); + cbor_array_push(*array_p, value_item); + cbor_decref(&value_item); + + if (!pctx->first_leaflist) { + if (((node->flags & LYD_DEFAULT) && (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG))) || + ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(node))) { + /* we have implicit OR explicit default node, get with-defaults module */ + wdmod = ly_ctx_get_module_implemented(LYD_CTX(node), "ietf-netconf-with-defaults"); + } + if (wdmod || node_has_printable_meta(node)) { + /* we will be printing metadata for these siblings */ + pctx->first_leaflist = node; + } + } + } + + if (cbor_print_array_is_last_inst(pctx, node)) { + /* add completed array to parent map */ + if (key) { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(*array_p) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + cbor_print_array_close(pctx); + *array_p = NULL; } + + return LY_SUCCESS; } /** - * @brief Count root level nodes, handling leaf-lists correctly + * @brief Print leaf-list's metadata or opaque nodes attributes. + * + * @param[in] pctx CBOR printer context. + * @param[in] parent_map CBOR map to add metadata to. + * @return LY_ERR value. */ -static size_t -cbor_count_root_nodes(const struct lyd_node *root) +static LY_ERR +cbor_print_meta_attr_leaflist(struct cborpr_ctx *pctx, cbor_item_t *parent_map) { - size_t count = 0; - const struct lyd_node *node; - const struct lysc_node *last_schema = NULL; - - LY_LIST_FOR(root, node) { - /* FIXED: Add null check for schema */ - if (!node->schema) { - continue; + const struct lyd_node *prev, *node, *iter; + const struct lys_module *wdmod = NULL, *iter_wdmod; + const struct lyd_node_opaq *opaq = NULL; + cbor_item_t *meta_array = NULL; + cbor_item_t *meta_map = NULL; + char *key = NULL; + + assert(pctx->first_leaflist); + + if (pctx->options & (LYD_PRINT_WD_ALL_TAG | LYD_PRINT_WD_IMPL_TAG)) { + /* get with-defaults module */ + wdmod = ly_ctx_get_module_implemented(pctx->ctx, "ietf-netconf-with-defaults"); + } + + /* node is the first instance of the leaf-list */ + for (node = pctx->first_leaflist, prev = pctx->first_leaflist->prev; + prev->next && matching_node(node, prev); + node = prev, prev = node->prev) {} + + /* create metadata array */ + meta_array = cbor_new_indefinite_array(); + LY_CHECK_RET(!meta_array, LY_EMEM); + + if (node->schema) { + key = cbor_print_member_name(pctx, node, 1); + } else { + opaq = (struct lyd_node_opaq *)node; + key = cbor_print_member_name2(pctx, lyd_parent(node), opaq->format, &opaq->name, 1); + } + LY_CHECK_ERR_RET(!key, cbor_decref(&meta_array), LY_EMEM); + + LY_LIST_FOR(node, iter) { + if (iter->schema && ((iter->flags & LYD_DEFAULT) || ((pctx->options & LYD_PRINT_WD_ALL_TAG) && lyd_is_default(iter)))) { + iter_wdmod = wdmod; + } else { + iter_wdmod = NULL; } - - /* For leaf-lists, only count the first occurrence */ - if (node->schema != last_schema) { - count++; - last_schema = node->schema; - } else if (!(node->schema->nodetype & LYS_LEAFLIST)) { - count++; + + if ((iter->schema && (node_has_printable_meta(iter) || iter_wdmod)) || (opaq && opaq->attr)) { + meta_map = cbor_new_indefinite_map(); + if (!meta_map) { + free(key); + cbor_decref(&meta_array); + return LY_EMEM; + } + + if (iter->schema) { + LY_CHECK_ERR_RET(cbor_print_metadata(pctx, iter, iter_wdmod, meta_map), + free(key); cbor_decref(&meta_array); cbor_decref(&meta_map), LY_EINVAL); + } else { + LY_CHECK_ERR_RET(cbor_print_attribute(pctx, (struct lyd_node_opaq *)iter, meta_map), + free(key); cbor_decref(&meta_array); cbor_decref(&meta_map), LY_EINVAL); + } + + cbor_array_push(meta_array, meta_map); + cbor_decref(&meta_map); + } else { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + cbor_array_push(meta_array, null_item); + cbor_decref(&null_item); + } + + if (!matching_node(iter, iter->next)) { + break; } } - - return count; + + /* add metadata array to parent map */ + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(meta_array) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + + return LY_SUCCESS; } /** - * @brief Main function to print data tree in CBOR format + * @brief Print opaq data node including its attributes. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Opaq node to print. + * @param[in] parent_map CBOR map to add the node to. + * @param[in,out] array_p Pointer to the array being built (for leaf-lists/lists). + * @return LY_ERR value. */ -LY_ERR -cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options) +static LY_ERR +cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_item_t *parent_map, cbor_item_t **array_p) { - LY_ERR ret = LY_SUCCESS; - struct cborpr_ctx ctx = {0}; - const struct lyd_node *node; - const struct lysc_node *last_schema = NULL; - size_t root_count; - unsigned char *cbor_data = NULL; - size_t cbor_data_len = 0; - - if (!out) { - return LY_EINVAL; + ly_bool first = 1, last = 1; + uint32_t hints; + char *key = NULL; + cbor_item_t *value_item = NULL; + + if (node->hints == LYD_HINT_DATA) { + /* useless and confusing hints */ + hints = 0; + } else { + hints = node->hints; } - - /* Initialize context */ - ctx.out = out; - ctx.root = root; - ctx.options = options; - ctx.level = 0; - - if (root) { - ctx.ctx = LYD_CTX(root); - - /* Count root level nodes */ - root_count = cbor_count_root_nodes(root); - - /* Debug: Print what we're processing */ - fprintf(stderr, "DEBUG: Processing %zu root nodes\n", root_count); - - /* Create root map */ - ctx.root_item = cbor_new_definite_map(root_count); - if (!ctx.root_item) { - fprintf(stderr, "DEBUG: Failed to create root map\n"); - ret = LY_EMEM; - goto cleanup; + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + if (node->prev->next && matching_node(node->prev, &node->node)) { + first = 0; } - - /* Process all root nodes */ - LY_LIST_FOR(root, node) { - /* FIXED: Add null check for schema */ - if (!node->schema) { - fprintf(stderr, "DEBUG: Skipping node with NULL schema\n"); - continue; + if (node->next && matching_node(&node->node, node->next)) { + last = 0; + } + } + + if (first) { + key = cbor_print_member_name2(pctx, pctx->parent, node->format, &node->name, 0); + LY_CHECK_RET(!key, LY_EMEM); + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + *array_p = cbor_new_indefinite_array(); + LY_CHECK_ERR_RET(!*array_p, free(key), LY_EMEM); + LY_CHECK_ERR_RET(cbor_print_array_open(pctx, &node->node), free(key), LY_EINVAL); + } + } + + if (node->child || (hints & LYD_NODEHINT_LIST) || (hints & LYD_NODEHINT_CONTAINER)) { + cbor_item_t *inner_map = NULL; + LY_CHECK_ERR_RET(cbor_print_inner(pctx, &node->node, &inner_map), free(key), LY_EINVAL); + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + cbor_array_push(*array_p, inner_map); + cbor_decref(&inner_map); + } else { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(inner_map) + }; + free(key); + key = NULL; + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; } - - /* Skip duplicate leaf-list entries (they are handled together) */ - if ((node->schema->nodetype & LYS_LEAFLIST) && (node->schema == last_schema)) { - continue; + } + } else { + if (hints & LYD_VALHINT_EMPTY) { + value_item = cbor_new_definite_array(1); + if (value_item) { + cbor_item_t *null_item = cbor_build_ctrl(CBOR_CTRL_NULL); + if (null_item) { + cbor_array_push(value_item, null_item); + cbor_decref(&null_item); + } } - - fprintf(stderr, "DEBUG: Processing node: %s\n", node->schema->name); - - ctx.root = node; - ret = cbor_print_node(&ctx, node, ctx.root_item); - if (ret != LY_SUCCESS) { - fprintf(stderr, "DEBUG: Failed to print node: %s\n", node->schema->name); - goto cleanup; + } else if ((hints & (LYD_VALHINT_BOOLEAN | LYD_VALHINT_DECNUM)) && !(hints & LYD_VALHINT_NUM64)) { + if (strcmp(node->value, "true") == 0) { + value_item = cbor_build_bool(true); + } else if (strcmp(node->value, "false") == 0) { + value_item = cbor_build_bool(false); + } else { + value_item = cbor_build_string(node->value); } - - last_schema = node->schema; - - /* Break if not printing siblings */ - if (!(options & LYD_PRINT_WITHSIBLINGS)) { - break; + } else { + /* string or a large number */ + value_item = cbor_build_string(node->value); + } + + LY_CHECK_ERR_RET(!value_item, free(key), LY_EMEM); + + if (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST)) { + cbor_array_push(*array_p, value_item); + cbor_decref(&value_item); + } else { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(value_item) + }; + free(key); + key = NULL; + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; } } - } else { - fprintf(stderr, "DEBUG: Empty data tree\n"); - /* Empty data tree - create empty map */ - ctx.root_item = cbor_new_definite_map(0); - if (!ctx.root_item) { - ret = LY_EMEM; - goto cleanup; + + if (!(hints & LYD_NODEHINT_LEAFLIST)) { + /* attributes */ + cbor_print_attributes(pctx, (const struct lyd_node *)node, parent_map, 0); + } else if (!pctx->first_leaflist && node->attr) { + /* attributes printed later */ + pctx->first_leaflist = &node->node; } } - - /* Serialize CBOR to bytes */ - cbor_data_len = cbor_serialize_alloc(ctx.root_item, &cbor_data, &cbor_data_len); - if (cbor_data_len == 0 || !cbor_data) { - fprintf(stderr, "DEBUG: Failed to serialize CBOR or got 0 bytes\n"); - ret = LY_EMEM; - goto cleanup; - } - - fprintf(stderr, "DEBUG: Generated %zu bytes of CBOR data\n", cbor_data_len); - - /* Write to output using ly_print_ macro */ - ly_print_(out, "%.*s", (int)cbor_data_len, cbor_data); -cleanup: - if (ctx.root_item) { - cbor_decref(&ctx.root_item); + if (last && (hints & (LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST))) { + if (key) { + struct cbor_pair pair = { + .key = cbor_move(cbor_build_string(key)), + .value = cbor_move(*array_p) + }; + free(key); + + if (!cbor_map_add(parent_map, pair)) { + cbor_decref(&pair.key); + cbor_decref(&pair.value); + return LY_EMEM; + } + } + cbor_print_array_close(pctx); + *array_p = NULL; } - if (cbor_data) { - free(cbor_data); + + if (key) { + free(key); } - - return ret; + + return LY_SUCCESS; } /** - * @brief Print data subtree in CBOR format + * @brief Print all the types of data node including its metadata. + * + * @param[in] pctx CBOR printer context. + * @param[in] node Data node to print. + * @param[in] parent_map CBOR map to add the node to. + * @return LY_ERR value. */ -LY_ERR -cbor_print_tree(struct ly_out *out, const struct lyd_node *root, uint32_t options, size_t max_depth) +static LY_ERR +cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) { - /* For now, ignore max_depth and use the regular print function */ - /* TODO: Implement depth limiting if needed */ - (void)max_depth; - return cbor_print_data(out, root, options); + static cbor_item_t *array = NULL; + + if (!lyd_node_should_print(node, pctx->options)) { + if (cbor_print_array_is_last_inst(pctx, node)) { + cbor_print_array_close(pctx); + } + return LY_SUCCESS; + } + + if (!node->schema) { + LY_CHECK_RET(cbor_print_opaq(pctx, (const struct lyd_node_opaq *)node, parent_map, &array)); + } else { + switch (node->schema->nodetype) { + case LYS_RPC: + case LYS_ACTION: + case LYS_NOTIF: + case LYS_CONTAINER: + LY_CHECK_RET(cbor_print_container(pctx, node, parent_map)); + break; + case LYS_LEAF: + LY_CHECK_RET(cbor_print_leaf(pctx, node, parent_map)); + break; + case LYS_LEAFLIST: + case LYS_LIST: + LY_CHECK_RET(cbor_print_leaf_list(pctx, node, parent_map, &array)); + break; + case LYS_ANYDATA: + case LYS_ANYXML: + LY_CHECK_RET(cbor_print_any(pctx, node, parent_map)); + break; + default: + LOGINT(pctx->ctx); + return EXIT_FAILURE; + } + } + + if (pctx->first_leaflist && !matching_node(node->next, pctx->first_leaflist)) { + cbor_print_meta_attr_leaflist(pctx, parent_map); + pctx->first_leaflist = NULL; + } + + return LY_SUCCESS; } -/** - * @brief Print all data trees in CBOR format - */ LY_ERR -cbor_print_all(struct ly_out *out, const struct lyd_node *root, uint32_t options) +cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t options) { - return cbor_print_data(out, root, options); -} \ No newline at end of file + const struct lyd_node *node; + struct cborpr_ctx pctx = {0}; + unsigned char *buffer = NULL; + size_t buffer_size = 0; + + if (!root) { + /* empty data - print empty map */ + cbor_item_t *empty_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!empty_map, LY_EMEM); + + buffer_size = cbor_serialize_alloc(empty_map, &buffer, &buffer_size); + cbor_decref(&empty_map); + + if (buffer_size == 0) { + return LY_EMEM; + } + + ly_write_(out, (const char *)buffer, buffer_size); + free(buffer); + ly_print_flush(out); + return LY_SUCCESS; + } + + pctx.out = out; + pctx.parent = NULL; + pctx.options = options; + pctx.ctx = LYD_CTX(root); + + /* create root map */ + pctx.root_map = cbor_new_indefinite_map(); + LY_CHECK_RET(!pctx.root_map, LY_EMEM); + + /* print content */ + LY_LIST_FOR(root, node) { + pctx.root = node; + LY_CHECK_ERR_RET(cbor_print_node(&pctx, node, pctx.root_map), + cbor_decref(&pctx.root_map); ly_set_erase(&pctx.open, NULL), LY_EINVAL); + if (!(options & LYD_PRINT_WITHSIBLINGS)) { + break; + } + } + + /* serialize CBOR to buffer */ + buffer_size = cbor_serialize_alloc(pctx.root_map, &buffer, &buffer_size); + cbor_decref(&pctx.root_map); + + if (buffer_size == 0) { + ly_set_erase(&pctx.open, NULL); + return LY_EMEM; + } + + /* write to output */ + ly_write_(out, (const char *)buffer, buffer_size); + free(buffer); + + assert(!pctx.open.count); + ly_set_erase(&pctx.open, NULL); + + ly_print_flush(out); + return LY_SUCCESS; +} + +#endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file From 675698926d89841bc66f40760e354891da97313e Mon Sep 17 00:00:00 2001 From: Meher C Date: Sat, 14 Mar 2026 02:59:45 +0000 Subject: [PATCH 7/8] fix: address PR review and migrate parser_cbor.c to current API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add FindCBOR.cmake module for proper CBOR dependency detection - Remove debug code and leftover artifacts - Revert parser_json.c to upstream (undo unintended changes) - Fix CBOR struct layout in parser_internal.h, tree_data.c, printer_cbor.c - Migrate parser_cbor.c to current upstream API: - Rewrite lydcbor_get_snode to use lys_find_child_node - Add lnode parameter to all LOGVAL calls - Fix LOG_LOCSET/LOG_LOCBACK to single-arg macros - Fix lyd_parser_create_term signature (add parent, value_size_bits) - Fix lyd_parser_create_meta signature (add lnode, value_size_bits) - Fix lyd_create_any to new 7-arg signature - Remove lyplg_ext_insert (function removed upstream) - Fix lyd_node_any access (value.tree → child) - Add tree_schema_internal.h include --- CMakeLists.txt | 55 +-- CMakeModules/FindCBOR.cmake | 37 ++ src/lcbor.c | 20 +- src/lcbor.h | 18 +- src/parser_cbor.c | 429 ++++++---------------- src/parser_internal.h | 17 +- src/parser_json.c | 708 +++++++++++++++++++----------------- src/printer_cbor.c | 93 ++--- src/printer_data.c | 2 + src/tree_data.c | 10 +- 10 files changed, 606 insertions(+), 783 deletions(-) create mode 100644 CMakeModules/FindCBOR.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a2d2a333a..971c730c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -257,7 +257,6 @@ option(ENABLE_YANGLINT_INTERACTIVE "Enable interactive CLI yanglint" ON) option(ENABLE_TOOLS "Build binary tools 'yanglint' and 'yangre'" ON) option(ENABLE_COMMON_TARGETS "Define common custom target names such as 'doc' or 'uninstall', may cause conflicts when using add_subdirectory() to build this project" ON) option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON) -option(ENABLE_CBOR_SUPPORT "Enable CBOR support with libcbor" ON) set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/libyang" CACHE STRING "Directory where to copy the YANG modules to") if(ENABLE_INTERNAL_DOCS) @@ -325,40 +324,14 @@ if(ENABLE_COVERAGE) gen_coverage_enable(${ENABLE_TESTS}) endif() -if(ENABLE_CBOR_SUPPORT) - find_package(PkgConfig) - if(PKG_CONFIG_FOUND) - pkg_check_modules(LIBCBOR REQUIRED libcbor) - if(LIBCBOR_FOUND) - message(STATUS "libcbor found, enabling CBOR support") - add_definitions(-DENABLE_CBOR_SUPPORT) - include_directories(${LIBCBOR_INCLUDE_DIRS}) - # Add CBOR parser files to the library sources - list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) - list(APPEND headers src/lcbor.h) - # Add CBOR files to format sources - list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) - else() - message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") - endif() - else() - # Fallback to find_path and find_library if pkg-config is not available - find_path(LIBCBOR_INCLUDE_DIR cbor.h) - find_library(LIBCBOR_LIBRARY cbor) - if(LIBCBOR_INCLUDE_DIR AND LIBCBOR_LIBRARY) - message(STATUS "libcbor found via find_path/find_library, enabling CBOR support") - add_definitions(-DENABLE_CBOR_SUPPORT) - include_directories(${LIBCBOR_INCLUDE_DIR}) - set(LIBCBOR_LIBRARIES ${LIBCBOR_LIBRARY}) - # Add CBOR parser files to the library sources - list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) - list(APPEND headers src/lcbor.h) - # Add CBOR files to format sources - list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) - else() - message(FATAL_ERROR "libcbor not found! Please install libcbor development package or disable CBOR support with -DENABLE_CBOR_SUPPORT=OFF") - endif() - endif() +find_package(CBOR) +if(CBOR_FOUND) + set(CBOR_SUPPORT ON) + list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) + list(APPEND headers src/lcbor.h) + list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) +else() + message(STATUS "libcbor not found, CBOR support disabled") endif() if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") @@ -465,9 +438,15 @@ find_package(PCRE2 10.21 REQUIRED) include_directories(${PCRE2_INCLUDE_DIRS}) target_link_libraries(yang ${PCRE2_LIBRARIES}) -# link libcbor if CBOR support is enabled -if(ENABLE_CBOR_SUPPORT) - target_link_libraries(yang ${LIBCBOR_LIBRARIES}) +# link libcbor if found +if(CBOR_FOUND) + if(TARGET yangobj) + target_compile_definitions(yangobj PRIVATE ENABLE_CBOR_SUPPORT) + target_include_directories(yangobj PRIVATE ${CBOR_INCLUDE_DIR}) + endif() + target_compile_definitions(yang PRIVATE ENABLE_CBOR_SUPPORT) + target_include_directories(yang PRIVATE ${CBOR_INCLUDE_DIR}) + target_link_libraries(yang ${CBOR_LIBRARY}) endif() # XXHash include and library diff --git a/CMakeModules/FindCBOR.cmake b/CMakeModules/FindCBOR.cmake new file mode 100644 index 000000000..14478f3ac --- /dev/null +++ b/CMakeModules/FindCBOR.cmake @@ -0,0 +1,37 @@ +# Try to find libcbor +# Once done this will define +# +# Read-Only variables: +# CBOR_FOUND - system has libcbor +# CBOR_INCLUDE_DIR - the libcbor include directory +# CBOR_LIBRARY - Link these to use libcbor + +find_path(CBOR_INCLUDE_DIR + NAMES + cbor.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include +) + +find_library(CBOR_LIBRARY + NAMES + cbor + libcbor + PATHS + /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(CBOR FOUND_VAR CBOR_FOUND REQUIRED_VARS CBOR_INCLUDE_DIR CBOR_LIBRARY) diff --git a/src/lcbor.c b/src/lcbor.c index 097ea7eec..03ea9ba59 100644 --- a/src/lcbor.c +++ b/src/lcbor.c @@ -1,5 +1,5 @@ /** - * @file lcbor.h + * @file lcbor.c * @author MeherRushi * @brief CBOR data parser for libyang (abstraction over libcbor) * @@ -12,8 +12,6 @@ * https://opensource.org/licenses/BSD-3-Clause */ -#ifdef ENABLE_CBOR_SUPPORT - #include #include #include @@ -55,8 +53,10 @@ lycbor_token2str(enum cbor_type cbortype) */ void lycbor_ctx_free(struct lycbor_ctx *cborctx) { - if (cborctx) - { + if (cborctx) { + if (cborctx->cbor_data) { + cbor_decref(&cborctx->cbor_data); + } free(cborctx); } } @@ -108,24 +108,22 @@ lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **c cborctx->format = format; /* load and parse CBOR data */ - cborctx->cbor_data = cbor_load(in->current, in->length, &result); + cborctx->cbor_data = cbor_load((cbor_data)in->current, in->length, &result); if (!cborctx->cbor_data) { - LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data."); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Failed to parse CBOR data."); free(cborctx); return LY_EVALID; } if (result.error.code != CBOR_ERR_NONE) { - LOGVAL(ctx, LYVE_SYNTAX, "CBOR parsing error (code %d).", result.error.code); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR parsing error (code %d).", result.error.code); cbor_decref(&cborctx->cbor_data); free(cborctx); return LY_EVALID; } /* input line logging */ - ly_log_location(NULL, NULL, NULL, in); + ly_log_location(NULL, NULL, in); *cborctx_p = cborctx; return ret; } - -#endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file diff --git a/src/lcbor.h b/src/lcbor.h index 838077ca8..ab1eba52e 100644 --- a/src/lcbor.h +++ b/src/lcbor.h @@ -12,10 +12,8 @@ * https://opensource.org/licenses/BSD-3-Clause */ -#ifndef LY_LCBOR_H_ -#define LY_LCBOR_H_ - -#ifdef ENABLE_CBOR_SUPPORT +#ifndef LY_CBOR_H_ +#define LY_CBOR_H_ #include #include @@ -53,6 +51,14 @@ struct lycbor_ctx { } backup; }; +/** + * @brief Get a human-readable name for a CBOR type. + * + * @param[in] cbortype CBOR type. + * @return String representation of the CBOR type. + */ +const char *lycbor_token2str(enum cbor_type cbortype); + /** * @brief Create new CBOR context for parsing. * @@ -72,6 +78,4 @@ lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **c void lycbor_ctx_free(struct lycbor_ctx *cborctx); -#endif /* ENABLE_CBOR_SUPPORT */ - -#endif /* LY_LCBOR_H_ */ \ No newline at end of file +#endif /* LY_CBOR_H_ */ diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 064dbe62d..9753c5017 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -14,10 +14,9 @@ #define _GNU_SOURCE -#ifdef ENABLE_CBOR_SUPPORT - #include #include +#include #include #include @@ -36,156 +35,9 @@ #include "tree_data.h" #include "tree_data_internal.h" #include "tree_schema.h" +#include "tree_schema_internal.h" #include "validation.h" -#include -#include -void print_json(cbor_item_t *item); - -void print_json_string(const cbor_item_t *item) -{ - size_t length = cbor_string_length(item); - char *str = (char *)cbor_string_handle(item); - printf("\"%.*s\"", (int)length, str); -} - -void print_json_array(const cbor_item_t *item) -{ - printf("["); - size_t size = cbor_array_size(item); - cbor_item_t **handle = cbor_array_handle(item); - - for (size_t i = 0; i < size; ++i) - { - print_json(handle[i]); - if (i < size - 1) - printf(", "); - } - - printf("]"); -} - -void print_json_map(const cbor_item_t *item) -{ - printf("{"); - size_t size = cbor_map_size(item); - struct cbor_pair *pairs = cbor_map_handle(item); - - for (size_t i = 0; i < size; ++i) - { - print_json(pairs[i].key); - printf(": "); - print_json(pairs[i].value); - if (i < size - 1) - printf(", "); - } - - printf("}"); -} - -void print_json_number(const cbor_item_t *item) -{ - if (cbor_isa_uint(item)) - { - printf("%lu", cbor_get_uint64(item)); - } - else if (cbor_isa_negint(item)) - { - printf("-%lu", cbor_get_uint64(item) + 1); - } - else if (cbor_isa_float_ctrl(item)) - { - if (cbor_float_get_width(item) == CBOR_FLOAT_64) - { - printf("%f", cbor_float_get_float8(item)); - } - else if (cbor_float_get_width(item) == CBOR_FLOAT_32) - { - printf("%f", cbor_float_get_float4(item)); - } - else if (cbor_float_get_width(item) == CBOR_FLOAT_16) - { - printf("%f", cbor_float_get_float2(item)); - } - } -} - -void print_json_bool(const cbor_item_t *item) -{ - printf(cbor_is_bool(item) && cbor_ctrl_value(item) ? "true" : "false"); -} - -void print_json_null(const cbor_item_t *item) -{ - printf("null"); -} - -void print_json(cbor_item_t *item) -{ - if (!item) - { - printf("null"); - return; - } - - if (cbor_isa_map(item)) - { - print_json_map(item); - } - else if (cbor_isa_array(item)) - { - print_json_array(item); - } - else if (cbor_isa_string(item)) - { - print_json_string(item); - } - else if (cbor_isa_uint(item) || cbor_isa_negint(item)) - { - print_json_number(item); - } - else if (cbor_isa_float_ctrl(item)) - { - // Check if it's a control value (null, undefined, true, false) - if (cbor_float_get_width(item) == CBOR_FLOAT_0) - { - uint8_t ctrl = cbor_ctrl_value(item); - if (ctrl == 20) - { - printf("false"); - } - else if (ctrl == 21) - { - printf("true"); - } - else if (ctrl == 22) - { - printf("null"); - } - else if (ctrl == 23) - { - printf("undefined"); - } - else - { - printf("null"); // unknown control value - } - } - else - { - print_json_number(item); - } - } - else if (cbor_is_bool(item)) - { - print_json_bool(item); - } - else - { - printf("null"); // fallback for truly unsupported types - } -} - /** * @brief Free the CBOR data parser context. * @@ -196,9 +48,9 @@ lyd_cbor_ctx_free(struct lyd_ctx *lydctx) { struct lyd_cbor_ctx *ctx = (struct lyd_cbor_ctx *)lydctx; - if (lydctx) - { + if (lydctx) { lyd_ctx_free(lydctx); + ly_set_erase(&ctx->ext_node, NULL); lycbor_ctx_free(ctx->cborctx); free(ctx); } @@ -347,113 +199,68 @@ static LY_ERR lydcbor_get_snode(struct lyd_cbor_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name, size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) { - LY_ERR ret = LY_SUCCESS, r; + LY_ERR r; struct lys_module *mod = NULL; + const struct lysc_node *sparent; uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; *snode = NULL; - *ext = NULL; - - /* get the element module, prefer parent context because of extensions */ - if (prefix_len) - { - mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->cborctx->ctx, prefix, prefix_len); + if (ext) { + *ext = NULL; } - else if (parent) - { - if (parent->schema) - { - mod = parent->schema->module; - } + + /* try to find parent schema node */ + if (parent && parent->schema && !(parent->schema->nodetype & LYD_NODE_ANY)) { + sparent = parent->schema; + } else { + sparent = NULL; } - else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Top-level CBOR object member \"%.*s\" must be namespace-qualified.", - (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name); - ret = LY_EVALID; - goto cleanup; + if (!prefix_len) { + prefix = NULL; } - if (!mod) - { - /* check for extension data */ - r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_CBOR, NULL, name, name_len, snode, ext); - if (r != LY_ENOT) - { - ret = r; - goto cleanup; - } + r = lys_find_child_node(parent ? LYD_CTX(parent) : lydctx->cborctx->ctx, sparent, NULL, prefix, prefix_len, + LY_VALUE_CBOR, NULL, name, name_len, getnext_opts, snode, ext); + LY_CHECK_RET(r && (r != LY_ENOT), r); - /* unknown module */ - if (lydctx->parse_opts & LYD_PARSE_STRICT) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix); - ret = LY_EVALID; - goto cleanup; - } + if (!r) { + /* check that schema node is valid and can be used */ + LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode)); + return LY_SUCCESS; } - /* get the schema node */ - if (mod && (!parent || parent->schema)) - { - if (!parent && lydctx->ext) - { - *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts); - } - else - { - *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts); + /* generate error, find the module */ + if (prefix_len) { + mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->cborctx->ctx, prefix, prefix_len); + } else if (parent) { + if (parent->schema) { + mod = parent->schema->module; } - if (!*snode) - { - /* check for extension data */ - r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_CBOR, NULL, name, name_len, snode, ext); - if (r != LY_ENOT) - { - ret = r; - goto cleanup; - } + } else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_SYNTAX, + "Top-level CBOR object member \"%.*s\" must be namespace-qualified.", + (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name); + return LY_EVALID; + } + if (!(lydctx->parse_opts & LYD_PARSE_STRICT)) { + return LY_SUCCESS; + } - /* unknown data node */ - printf("checkpoint1-web\n"); - if (lydctx->parse_opts & LYD_PARSE_STRICT) - { - if (parent) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", - (int)name_len, name, LYD_NAME(parent)); - } - else if (lydctx->ext) - { - if (lydctx->ext->argument) - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, - "Node \"%.*s\" not found in the \"%s\" %s extension instance.", - (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name); - } - else - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.", - (int)name_len, name, lydctx->ext->def->name); - } - } - else - { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", - (int)name_len, name, mod->name); - } - ret = LY_EVALID; - goto cleanup; - } - } - else - { - /* check that schema node is valid and can be used */ - ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode); - } + if (!mod) { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_REFERENCE, + "No module named \"%.*s\" in the context.", (int)prefix_len, prefix); + return LY_EVALID; } -cleanup: - return ret; + if (sparent) { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_REFERENCE, + "Node \"%.*s\" not found as a child of \"%s\" node.", + (int)name_len, name, sparent->name); + } else { + LOGVAL(lydctx->cborctx->ctx, parent, LYVE_REFERENCE, + "Node \"%.*s\" not found in the \"%s\" module.", + (int)name_len, name, mod->name); + } + return LY_EVALID; } /** @@ -520,7 +327,7 @@ lydcbor_value_type_hint(struct lyd_cbor_ctx *lydctx, const cbor_item_t *item, ui return LY_SUCCESS; } } - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR value or [null], but array found."); + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Expected CBOR value or [null], but array found."); return LY_EINVAL; } else if (type == CBOR_TYPE_STRING) @@ -546,7 +353,7 @@ lydcbor_value_type_hint(struct lyd_cbor_ctx *lydctx, const cbor_item_t *item, ui } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unexpected CBOR control value."); + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Unexpected CBOR control value."); return LY_EINVAL; } } @@ -557,7 +364,7 @@ lydcbor_value_type_hint(struct lyd_cbor_ctx *lydctx, const cbor_item_t *item, ui } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Unexpected CBOR data type."); + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Unexpected CBOR data type."); return LY_EINVAL; } @@ -636,7 +443,7 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) *str_len = 0; break; default: - LOGVAL(NULL, LYVE_SYNTAX, "Unsupported CBOR control value"); + LOGVAL(NULL, NULL, LYVE_SYNTAX, "Unsupported CBOR control value"); ret = LY_EVALID; break; } @@ -654,7 +461,7 @@ lydcbor_item_to_string(const cbor_item_t *item, char **str_val, size_t *str_len) } break; default: - LOGVAL(NULL, LYVE_SYNTAX, "Unsupported CBOR data type %d", cbor_typeof(item)); + LOGVAL(NULL, NULL, LYVE_SYNTAX, "Unsupported CBOR data type %d", cbor_typeof(item)); ret = LY_EVALID; break; } @@ -761,7 +568,7 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) continue; } - LOG_LOCSET(NULL, attr); + /* dnode-only location no longer supported by LOG_LOCSET */ log_location_items++; if (prev != meta_container->name.name) @@ -788,7 +595,7 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) { - LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", ((struct lyd_node_opaq *)node)->name.name); ret = LY_EVALID; goto cleanup; @@ -839,22 +646,22 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) if (mod) { ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, - meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value), - NULL, LY_VALUE_CBOR, NULL, meta->hints, node->schema); + meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value) * 8, + NULL, LY_VALUE_CBOR, NULL, meta->hints, node->schema, node); LY_CHECK_GOTO(ret, cleanup); } else if (lydctx->parse_opts & LYD_PARSE_STRICT) { if (meta->name.prefix) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", meta->name.name); } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", meta->name.name); } ret = LY_EVALID; @@ -872,13 +679,13 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) { if (instance > 1) { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu64 " to be coupled with %s metadata.", instance, meta_container->name.name); } else { - LOGVAL(lydctx->cborctx->ctx, LYVE_REFERENCE, "Missing CBOR data instance to be coupled with %s metadata.", + LOGVAL(lydctx->cborctx->ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance to be coupled with %s metadata.", meta_container->name.name); } ret = LY_EVALID; @@ -892,13 +699,13 @@ lydcbor_metadata_finish(struct lyd_cbor_ctx *lydctx, struct lyd_node **first_p) lyd_free_tree(attr); } - LOG_LOCBACK(0, log_location_items); + LOG_LOCBACK(0); log_location_items = 0; } cleanup: lydict_remove(lydctx->cborctx->ctx, prev); - LOG_LOCBACK(0, log_location_items); + LOG_LOCBACK(0); return ret; } @@ -935,7 +742,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st nodetype = snode ? snode->nodetype : LYS_CONTAINER; if (snode) { - LOG_LOCSET(snode, NULL); + LOG_LOCSET(snode); } type = cbor_typeof(cbor_meta); @@ -951,7 +758,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st instance++; if (!node || (node->schema != prev->schema)) { - LOGVAL(ctx, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); + LOGVAL(ctx, NULL, LYVE_REFERENCE, "Missing CBOR data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); rc = LY_EVALID; goto cleanup; } @@ -1013,7 +820,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st if (!cbor_isa_string(key_item)) { - LOGVAL(ctx, LYVE_SYNTAX, "Metadata key must be a string."); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata key must be a string."); rc = LY_EVALID; goto cleanup; } @@ -1024,14 +831,14 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st if (!name_len) { - LOGVAL(ctx, LYVE_SYNTAX, "Metadata in CBOR found with an empty name."); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata in CBOR found with an empty name."); free(key_str); rc = LY_EVALID; goto cleanup; } else if (!prefix_len) { - LOGVAL(ctx, LYVE_SYNTAX, "Metadata in CBOR must be namespace-qualified, missing prefix for \"%.*s\".", + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Metadata in CBOR must be namespace-qualified, missing prefix for \"%.*s\".", (int)key_len, key_str); free(key_str); rc = LY_EVALID; @@ -1039,7 +846,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st } else if (is_attr) { - LOGVAL(ctx, LYVE_SYNTAX, "Invalid format of Metadata identifier in CBOR, unexpected '@' in \"%.*s\"", + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid format of Metadata identifier in CBOR, unexpected '@' in \"%.*s\"", (int)key_len, key_str); free(key_str); rc = LY_EVALID; @@ -1052,7 +859,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st { if (lydctx->parse_opts & LYD_PARSE_STRICT) { - LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", + LOGVAL(ctx, NULL, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", (int)prefix_len, prefix, (int)name_len, name); free(key_str); rc = LY_EVALID; @@ -1078,7 +885,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st /* create metadata */ rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, str_val, - str_len, NULL, LY_VALUE_CBOR, NULL, val_hints, node->schema); + str_len * 8, NULL, LY_VALUE_CBOR, NULL, val_hints, node->schema, node); free(str_val); LY_CHECK_ERR_GOTO(rc, free(key_str), cleanup); @@ -1117,7 +924,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st goto cleanup; representation_error: - LOGVAL(ctx, LYVE_SYNTAX, + LOGVAL(ctx, NULL, LYVE_SYNTAX, "The attribute(s) of %s \"%s\" is expected to be represented as CBOR %s, but input data contains different type.", lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected); rc = LY_EVALID; @@ -1130,7 +937,7 @@ lydcbor_meta_attr(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st rc = r; } } - LOG_LOCBACK(snode ? 1 : 0, 0); + LOG_LOCBACK(snode ? 1 : 0); return rc; } @@ -1152,14 +959,7 @@ lydcbor_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, st return; } - if (ext) - { - lyplg_ext_insert(parent, *node_p); - } - else - { - lyd_insert_node(parent, first_p, *node_p, last); - } + lyd_insert_node(parent, first_p, *node_p, last); if (first_p) { if (parent) @@ -1258,7 +1058,7 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le LY_CHECK_GOTO(ret = lydcbor_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, cbor_value, node_p), cleanup); assert(*node_p); - LOG_LOCSET(NULL, *node_p); + /* dnode-only location no longer supported by LOG_LOCSET */ if ((type == CBOR_TYPE_ARRAY) && (cbor_array_size(cbor_value) == 1)) { @@ -1302,12 +1102,12 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le lydcbor_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); - LOG_LOCBACK(0, 1); + LOG_LOCBACK(0); LY_CHECK_GOTO(ret = lydcbor_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, item, node_p), cleanup); assert(*node_p); - LOG_LOCSET(NULL, *node_p); + /* dnode-only location no longer supported by LOG_LOCSET */ } } } @@ -1325,7 +1125,7 @@ lydcbor_parse_opaq(struct lyd_cbor_ctx *lydctx, const char *name, size_t name_le cleanup: if (*node_p) { - LOG_LOCBACK(0, 1); + LOG_LOCBACK(0); } return ret; } @@ -1478,11 +1278,11 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st if (type == CBOR_TYPE_MAP) { /* create node */ - r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node); + r = lyd_create_any(snode, NULL, NULL, 0, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); assert(*node); - LOG_LOCSET(NULL, *node); + /* dnode-only location no longer supported by LOG_LOCSET */ log_node = 1; /* parse data tree with correct options */ @@ -1500,20 +1300,26 @@ lydcbor_parse_any(struct lyd_cbor_ctx *lydctx, const struct lysc_node *snode, st LY_CHECK_ERR_GOTO(r, rc = r, cleanup); /* assign the data tree */ - ((struct lyd_node_any *)*node)->value.tree = child; + ((struct lyd_node_any *)*node)->child = child; child = NULL; } else { - /* store CBOR value directly */ - r = lyd_create_any(snode, cbor_value, LYD_ANYDATA_CBOR, 0, node); + /* convert CBOR scalar value to string and store */ + char *str_val = NULL; + size_t str_len = 0; + + r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + r = lyd_create_any(snode, NULL, str_val, LYD_VALHINT_STRING, 1, 0, node); + LY_CHECK_ERR_GOTO(r, free(str_val); rc = r, cleanup); } cleanup: if (log_node) { - LOG_LOCBACK(0, 1); + LOG_LOCBACK(0); } lydctx->parse_opts = prev_parse_opts; lydctx->int_opts = prev_int_opts; @@ -1547,7 +1353,7 @@ lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node LY_CHECK_RET(lyd_create_inner(snode, node)); /* use it for logging */ - LOG_LOCSET(NULL, *node); + /* dnode-only location no longer supported by LOG_LOCSET */ if (ext) { @@ -1579,7 +1385,7 @@ lydcbor_parse_instance_inner(struct lyd_cbor_ctx *lydctx, const struct lysc_node cleanup: lydctx->parse_opts = prev_parse_opts; - LOG_LOCBACK(0, 1); + LOG_LOCBACK(0); if (!(*node)->hash) { /* list without keys is unusable */ @@ -1617,7 +1423,7 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str char *str_val = NULL; size_t str_len = 0; - LOG_LOCSET(snode, NULL); + LOG_LOCSET(snode); r = lydcbor_data_check_opaq(lydctx, snode, cbor_value, &type_hints); if (r == LY_SUCCESS) @@ -1653,8 +1459,8 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str r = lydcbor_item_to_string(cbor_value, &str_val, &str_len); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, str_val, str_len, NULL, LY_VALUE_CBOR, - NULL, type_hints, node); + r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, parent, str_val, str_len * 8, + NULL, LY_VALUE_CBOR, NULL, type_hints, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } else if (snode->nodetype & LYD_NODE_INNER) @@ -1706,7 +1512,7 @@ lydcbor_parse_instance(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, str cleanup: free(str_val); - LOG_LOCBACK(1, 0); + LOG_LOCBACK(1); return rc; } @@ -1742,7 +1548,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l if (!pairs && map_size > 0) { - LOGVAL(ctx, LYVE_SYNTAX, "Invalid CBOR map structure"); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid CBOR map structure"); return LY_EVALID; } @@ -1756,7 +1562,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l if (!key_item || !value_item) { - LOGVAL(ctx, LYVE_SYNTAX, "Null key or value in CBOR map"); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Null key or value in CBOR map"); rc = LY_EVALID; goto cleanup; } @@ -1770,7 +1576,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* Get key string */ if (!cbor_isa_string(key_item)) { - LOGVAL(ctx, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR map key must be string for named identifier format"); rc = LY_EVALID; goto cleanup; } @@ -1808,7 +1614,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* parse as metadata */ if (!name_len && !prefix_len && !parent) { - LOGVAL(ctx, LYVE_SYNTAX, + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); r = LY_EVALID; free(key_str); @@ -1838,7 +1644,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l /* parse as an opaq node */ if (name_len == 0) { - LOGVAL(ctx, LYVE_SYNTAX, "CBOR object member name cannot be a zero-length string."); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "CBOR object member name cannot be a zero-length string."); r = LY_EVALID; free(key_str); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1954,7 +1760,7 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent, struct l goto cleanup; representation_error: - LOGVAL(ctx, LYVE_SYNTAX, "Expecting CBOR %s but %s \"%s\" is represented in input data differently.", + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Expecting CBOR %s but %s \"%s\" is represented in input data differently.", expected, lys_nodetype2str(snode->nodetype), snode->name); rc = LY_EVALID; if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) @@ -2005,7 +1811,7 @@ lyd_parse_cbor_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o if (!cbor_isa_map(lydctx->cborctx->cbor_data)) { /* expecting top-level map */ - LOGVAL(ctx, LYVE_SYNTAX, "Expected top-level CBOR map, but %d found.", cbortype); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Expected top-level CBOR map, but %d found.", cbortype); *lydctx_p = NULL; lyd_cbor_ctx_free((struct lyd_ctx *)lydctx); return LY_EVALID; @@ -2029,11 +1835,6 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st lydctx->int_opts = int_opts; lydctx->ext = ext; - // DEBUG: print the parsed CBOR data - printf("DEBUG: Parsed CBOR data:\n"); - print_json(lydctx->cborctx->cbor_data); - printf("\n"); - // END DEBUG /* find the operation node if it exists already */ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); @@ -2044,13 +1845,13 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st if ((int_opts & LYD_INTOPT_NO_SIBLINGS)) { - LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node."); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { - LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); + LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -2059,14 +1860,8 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st r = lydcbor_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (parse_opts & LYD_PARSE_SUBTREE) - { - /* subtree parsing not fully implemented for CBOR */ - if (subtree_sibling) - { - *subtree_sibling = 0; - } - } + /* parse_opts & LYD_PARSE_SUBTREE not implemented */ + (void)subtree_sibling; cleanup: /* there should be no unresolved types stored */ @@ -2085,5 +1880,3 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st } return rc; } - -#endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file diff --git a/src/parser_internal.h b/src/parser_internal.h index 590fd74e0..443dcf1ec 100644 --- a/src/parser_internal.h +++ b/src/parser_internal.h @@ -22,6 +22,7 @@ struct lyd_ctx; struct ly_in; +struct lycbor_ctx; struct lysp_ext_substmt; struct lysp_stmt; struct lysp_yang_ctx; @@ -175,13 +176,10 @@ struct lyd_lyb_ctx { }; }; -#ifdef ENABLE_CBOR_SUPPORT /** * @brief Internal context for CBOR data parser. */ -struct lyd_cbor_ctx -{ - const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ +struct lyd_cbor_ctx { uint32_t parse_opts; /**< various @ref dataparseroptions. */ uint32_t val_opts; /**< various @ref datavalidationoptions. */ uint32_t int_opts; /**< internal parser options */ @@ -190,19 +188,19 @@ struct lyd_cbor_ctx struct ly_set node_when; /**< set of nodes with "when" conditions */ struct ly_set node_types; /**< set of nodes with unresolved types */ struct ly_set meta_types; /**< set of metadata with unresolved types */ - struct ly_set ext_node; /**< set of nodes with extension instances to validate */ struct ly_set ext_val; /**< set of nested extension data to validate */ struct lyd_node *op_node; /**< if an operation is being parsed, its node */ const struct lys_module *val_getnext_ht_mod; struct ly_ht *val_getnext_ht; - + /* callbacks */ lyd_ctx_free_clb free; /**< destructor */ - struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ + const struct lysc_ext_instance *ext; /**< extension instance possibly changing document root context, NULL if none */ + struct ly_set ext_node; /**< set of nodes with extension instances to validate */ + struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */ const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */ }; -#endif /* ENABLE_CBOR_SUPPORT */ /** * @brief Parsed extension instance data to validate. @@ -354,7 +352,6 @@ LY_ERR lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent LY_ERR lyd_parse_lyb(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, struct lyd_ctx **lydctx_p); -#ifdef ENABLE_CBOR_SUPPORT /** * @brief Parse CBOR data into libyang data tree. * @@ -378,8 +375,6 @@ LY_ERR lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance * struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p); -#endif /* ENABLE_CBOR_SUPPORT */ - /** * @brief Validate eventTime date-and-time value. * diff --git a/src/parser_json.c b/src/parser_json.c index c0dc01568..9d09ac6d4 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -4,7 +4,7 @@ * @author Michal Vasko * @brief JSON data parser for libyang * - * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2020 - 2026 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. @@ -38,6 +38,9 @@ #include "tree_schema_internal.h" #include "validation.h" +static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, + struct ly_set *parsed); + /** * @brief Free the JSON data parser context. * @@ -174,7 +177,7 @@ lydjson_get_node_prefix(struct lyd_node *node, const char *local_prefix, size_t module_name = onode->name.prefix; break; } - node = lyd_parent(node); + node = node->parent; } *prefix_p = module_name; @@ -253,14 +256,37 @@ static LY_ERR lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *prefix, size_t prefix_len, const char *name, size_t name_len, struct lyd_node *parent, const struct lysc_node **snode, struct lysc_ext_instance **ext) { - LY_ERR ret = LY_SUCCESS, r; + LY_ERR r; struct lys_module *mod = NULL; + const struct lysc_node *sparent; uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0; *snode = NULL; - *ext = NULL; + if (ext) { + *ext = NULL; + } - /* get the element module, prefer parent context because of extensions */ + /* try to find parent schema node */ + if (parent && parent->schema && !(parent->schema->nodetype & LYD_NODE_ANY)) { + /* use only a schema parent */ + sparent = parent->schema; + } else { + sparent = NULL; + } + if (!prefix_len) { + prefix = NULL; + } + r = lys_find_child_node(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, sparent, NULL, prefix, prefix_len, + LY_VALUE_JSON, NULL, name, name_len, getnext_opts, snode, ext); + LY_CHECK_RET(r && (r != LY_ENOT), r); + + if (!r) { + /* check that schema node is valid and can be used */ + LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode)); + return LY_SUCCESS; + } + + /* generate error, find the module */ if (prefix_len) { mod = ly_ctx_get_module_implemented2(parent ? LYD_CTX(parent) : lydctx->jsonctx->ctx, prefix, prefix_len); } else if (parent) { @@ -268,80 +294,38 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref mod = parent->schema->module; } } else if (!(lydctx->int_opts & LYD_INTOPT_ANY)) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Top-level JSON object member \"%.*s\" must be namespace-qualified.", + LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_SYNTAX_JSON, + "Top-level JSON object member \"%.*s\" must be namespace-qualified.", (int)(is_attr ? name_len + 1 : name_len), is_attr ? name - 1 : name); - ret = LY_EVALID; - goto cleanup; + return LY_EVALID; + } + if (!(lydctx->parse_opts & LYD_PARSE_STRICT)) { + return LY_SUCCESS; } - if (!mod) { - /* check for extension data */ - r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext); - if (r != LY_ENOT) { - /* success or error */ - ret = r; - goto cleanup; - } + if (!mod) { /* unknown module */ - if (lydctx->parse_opts & LYD_PARSE_STRICT) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", (int)prefix_len, prefix); - ret = LY_EVALID; - goto cleanup; - } + LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_REFERENCE, "No module named \"%.*s\" in the context.", + (int)prefix_len, prefix); + return LY_EVALID; } - /* get the schema node */ - if (mod && (!parent || parent->schema)) { - if (!parent && lydctx->ext) { - *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts); - } else { - *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts); - } - if (!*snode) { - /* check for extension data */ - r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_JSON, NULL, name, name_len, snode, ext); - if (r != LY_ENOT) { - /* success or error */ - ret = r; - goto cleanup; - } - - /* unknown data node */ - printf("checkpoint1-json\n"); - if (lydctx->parse_opts & LYD_PARSE_STRICT) { - if (parent) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", - (int)name_len, name, LYD_NAME(parent)); - } else if (lydctx->ext) { - if (lydctx->ext->argument) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, - "Node \"%.*s\" not found in the \"%s\" %s extension instance.", - (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name); - } else { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.", - (int)name_len, name, lydctx->ext->def->name); - } - } else { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", - (int)name_len, name, mod->name); - } - ret = LY_EVALID; - goto cleanup; - } - } else { - /* check that schema node is valid and can be used */ - ret = lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode); - } + /* unknown node name */ + if (sparent) { + LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.", + (int)name_len, name, sparent->name); + } else { + LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.", + (int)name_len, name, mod->name); } - -cleanup: - return ret; + return LY_EVALID; } /** * @brief Get the hint for the data type parsers according to the current JSON parser context. * * @param[in] lydctx JSON data parser context. + * @param[in] snode Schema node for logging. * @param[in,out] status Pointer to the current context status, * in some circumstances the function manipulates with the context so the status is updated. * @param[out] type_hint_p Pointer to the variable to store the result. @@ -349,7 +333,8 @@ lydjson_get_snode(struct lyd_json_ctx *lydctx, ly_bool is_attr, const char *pref * @return LY_EINVAL in case of invalid context status not referring to a value. */ static LY_ERR -lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status_p, uint32_t *type_hint_p) +lydjson_value_type_hint(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, enum LYJSON_PARSER_STATUS *status_p, + uint32_t *type_hint_p) { struct lyjson_ctx *jsonctx = lydctx->jsonctx; @@ -359,16 +344,28 @@ lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS * /* only [null] */ LY_CHECK_RET(lyjson_ctx_next(jsonctx, status_p)); if (*status_p != LYJSON_NULL) { - LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, + if (snode) { + LOG_LOCSET(snode); + } + LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Expected JSON name/value or special name/[null], but input data contains name/[%s].", lyjson_token2str(*status_p)); + if (snode) { + LOG_LOCBACK(1); + } return LY_EINVAL; } LY_CHECK_RET(lyjson_ctx_next(jsonctx, NULL)); if (lyjson_ctx_status(jsonctx) != LYJSON_ARRAY_CLOSED) { - LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.", + if (snode) { + LOG_LOCSET(snode); + } + LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Expected array end, but input data contains %s.", lyjson_token2str(*status_p)); + if (snode) { + LOG_LOCBACK(1); + } return LY_EINVAL; } @@ -382,7 +379,13 @@ lydjson_value_type_hint(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS * } else if (*status_p == LYJSON_NULL) { *type_hint_p = 0; } else { - LOGVAL(jsonctx->ctx, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p)); + if (snode) { + LOG_LOCSET(snode); + } + LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Unexpected input data %s.", lyjson_token2str(*status_p)); + if (snode) { + LOG_LOCBACK(1); + } return LY_EINVAL; } @@ -459,9 +462,10 @@ lydjson_check_list(struct lyd_json_ctx *lydctx, const struct lysc_node *list) goto cleanup; } - rc = lydjson_value_type_hint(lydctx, &status, &hints); + rc = lydjson_value_type_hint(lydctx, snode, &status, &hints); LY_CHECK_GOTO(rc, cleanup); - rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, hints); + rc = ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len * 8, LY_VALUE_JSON, NULL, + hints); LY_CHECK_GOTO(rc, cleanup); /* key with a valid value, remove from the set */ @@ -528,15 +532,18 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno switch (snode->nodetype) { case LYS_LEAFLIST: case LYS_LEAF: + prev_lo = ly_temp_log_options(&temp_lo); + /* value may not be valid in which case we parse it as an opaque node */ - if ((ret = lydjson_value_type_hint(lydctx, &status, type_hint_p))) { - break; + if (lydjson_value_type_hint(lydctx, snode, &status, type_hint_p)) { + ret = LY_ENOT; } - prev_lo = ly_temp_log_options(&temp_lo); - if (ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len, LY_VALUE_JSON, NULL, *type_hint_p)) { + if (!ret && ly_value_validate(NULL, snode, jsonctx->value, jsonctx->value_len * 8, LY_VALUE_JSON, NULL, + *type_hint_p)) { ret = LY_ENOT; } + ly_temp_log_options(prev_lo); break; case LYS_LIST: @@ -548,7 +555,7 @@ lydjson_data_check_opaq(struct lyd_json_ctx *lydctx, const struct lysc_node *sno break; } } else if (snode->nodetype & LYD_NODE_TERM) { - ret = lydjson_value_type_hint(lydctx, &status, type_hint_p); + ret = lydjson_value_type_hint(lydctx, snode, &status, type_hint_p); } /* restore parser */ @@ -573,10 +580,8 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) { LY_ERR ret = LY_SUCCESS; struct lyd_node *node, *attr, *next, *meta_iter; - struct lysc_ext_instance *ext; uint64_t instance = 0; const char *prev = NULL; - uint32_t log_location_items = 0; /* finish linking metadata */ LY_LIST_FOR_SAFE(*first_p, next, attr) { @@ -592,9 +597,6 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) continue; } - LOG_LOCSET(NULL, attr); - log_location_items++; - if (prev != meta_container->name.name) { /* metas' names are stored in dictionary, so checking pointers must works */ lydict_remove(lydctx->jsonctx->ctx, prev); @@ -613,7 +615,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) } if (((struct lyd_node_opaq *)node)->hints & LYD_NODEHINT_LIST) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", + LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_SYNTAX, "Metadata container references a sibling list node %s.", ((struct lyd_node_opaq *)node)->name.name); ret = LY_EVALID; goto cleanup; @@ -644,7 +646,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) lydjson_parse_name(meta_container->name.name, strlen(meta_container->name.name), &name, &name_len, &prefix, &prefix_len, &is_attr); assert(is_attr); - lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, lyd_parent(*first_p), &snode, &ext); + lydjson_get_snode(lydctx, is_attr, prefix, prefix_len, name, name_len, (*first_p)->parent, &snode, NULL); if (snode != node->schema) { continue; @@ -663,18 +665,18 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) mod = ly_ctx_get_module_implemented(lydctx->jsonctx->ctx, meta->name.prefix); if (mod) { - ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, - meta->name.name, strlen(meta->name.name), meta->value, ly_strlen(meta->value), - NULL, LY_VALUE_JSON, NULL, meta->hints, node->schema); + ret = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, meta->name.name, + strlen(meta->name.name), meta->value, ly_strlen(meta->value) * 8, NULL, LY_VALUE_JSON, + NULL, meta->hints, node->schema, node); LY_CHECK_GOTO(ret, cleanup); } else if (lydctx->parse_opts & LYD_PARSE_STRICT) { if (meta->name.prefix) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, + LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, "Unknown (or not implemented) YANG module \"%s\" of metadata \"%s%s%s\".", meta->name.prefix, meta->name.prefix, ly_strlen(meta->name.prefix) ? ":" : "", meta->name.name); } else { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", + LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, "Missing YANG module of metadata \"%s\".", meta->name.name); } ret = LY_EVALID; @@ -683,7 +685,7 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) } /* add/correct flags */ - ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, ext); + ret = lyd_parser_set_data_flags(node, &node->meta, (struct lyd_ctx *)lydctx, NULL); LY_CHECK_GOTO(ret, cleanup); break; } @@ -692,11 +694,11 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) if (match != instance) { /* there is no corresponding data node for the metadata */ if (instance > 1) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, + LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu64 " to be coupled with %s metadata.", instance, meta_container->name.name); } else { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance to be coupled with %s metadata.", + LOGVAL(lydctx->jsonctx->ctx, attr, LYVE_REFERENCE, "Missing JSON data instance to be coupled with %s metadata.", meta_container->name.name); } ret = LY_EVALID; @@ -707,15 +709,10 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) } lyd_free_tree(attr); } - - LOG_LOCBACK(0, log_location_items); - log_location_items = 0; } cleanup: lydict_remove(lydctx->jsonctx->ctx, prev); - - LOG_LOCBACK(0, log_location_items); return ret; } @@ -723,15 +720,12 @@ lydjson_metadata_finish(struct lyd_json_ctx *lydctx, struct lyd_node **first_p) * @brief Parse a metadata member/attribute. * * @param[in] lydctx JSON data parser context. - * @param[in] snode Schema node of the metadata parent. - * @param[in] node Parent node in case the metadata is not forward-referencing (only LYD_NODE_TERM) - * so the data node does not exists. In such a case the metadata is stored in the context for the later - * processing by lydjson_metadata_finish(). + * @param[in] node Parent node. * @return LY_SUCCESS on success * @return Various LY_ERR values in case of failure. */ static LY_ERR -lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lyd_node *node) +lydjson_meta_attr(struct lyd_json_ctx *lydctx, struct lyd_node *node) { LY_ERR rc = LY_SUCCESS, r; enum LYJSON_PARSER_STATUS status; @@ -747,12 +741,7 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st uint32_t instance = 0, val_hints; uint16_t nodetype; - assert(snode || node); - - nodetype = snode ? snode->nodetype : LYS_CONTAINER; - if (snode) { - LOG_LOCSET(snode, NULL); - } + nodetype = node->schema ? node->schema->nodetype : LYS_CONTAINER; /* move to the second item in the name/X pair */ LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup); @@ -775,8 +764,10 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st LY_CHECK_GOTO((status != LYJSON_OBJECT) && (status != LYJSON_NULL), representation_error); if (!node || (node->schema != prev->schema)) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu32 + LOG_LOCSET(prev->schema); + LOGVAL(lydctx->jsonctx->ctx, NULL, LYVE_REFERENCE, "Missing JSON data instance #%" PRIu32 " of %s:%s to be coupled with metadata.", instance, prev->schema->module->name, prev->schema->name); + LOG_LOCBACK(1); rc = LY_EVALID; goto cleanup; } @@ -822,16 +813,16 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st lyjson_ctx_give_dynamic_value(lydctx->jsonctx, &dynamic_prefname); if (!name_len) { - LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name); + LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "Metadata in JSON found with an empty name, followed by: %.10s", name); rc = LY_EVALID; goto cleanup; } else if (!prefix_len) { - LOGVAL(ctx, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".", + LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "Metadata in JSON must be namespace-qualified, missing prefix for \"%.*s\".", (int)lydctx->jsonctx->value_len, lydctx->jsonctx->value); rc = LY_EVALID; goto cleanup; } else if (is_attr) { - LOGVAL(ctx, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"", + LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "Invalid format of the Metadata identifier in JSON, unexpected '@' in \"%.*s\"", (int)lydctx->jsonctx->value_len, lydctx->jsonctx->value); rc = LY_EVALID; goto cleanup; @@ -841,7 +832,8 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st mod = ly_ctx_get_module_implemented2(ctx, prefix, prefix_len); if (!mod) { if (lydctx->parse_opts & LYD_PARSE_STRICT) { - LOGVAL(ctx, LYVE_REFERENCE, "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", + LOGVAL(ctx, prev, LYVE_REFERENCE, + "Prefix \"%.*s\" of the metadata \"%.*s\" does not match any module in the context.", (int)prefix_len, prefix, (int)name_len, name); rc = LY_EVALID; goto cleanup; @@ -860,12 +852,13 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st LY_CHECK_GOTO(rc = lyjson_ctx_next(lydctx->jsonctx, &status), cleanup); /* get value hints */ - LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, &status, &val_hints), cleanup); + LY_CHECK_GOTO(rc = lydjson_value_type_hint(lydctx, node->schema, &status, &val_hints), cleanup); if (node->schema) { /* create metadata */ - rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, lydctx->jsonctx->value, - lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, val_hints, node->schema); + rc = lyd_parser_create_meta((struct lyd_ctx *)lydctx, node, NULL, mod, name, name_len, + lydctx->jsonctx->value, lydctx->jsonctx->value_len * 8, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, + NULL, val_hints, node->schema, node); LY_CHECK_GOTO(rc, cleanup); /* add/correct flags */ @@ -904,7 +897,7 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st goto cleanup; representation_error: - LOGVAL(ctx, LYVE_SYNTAX_JSON, + LOGVAL(ctx, prev, LYVE_SYNTAX_JSON, "The attribute(s) of %s \"%s\" is expected to be represented as JSON %s, but input data contains @%s/%s.", lys_nodetype2str(nodetype), node ? LYD_NAME(node) : LYD_NAME(prev), expected, lyjson_token2str(status), in_parent ? "" : "name"); @@ -918,46 +911,9 @@ lydjson_meta_attr(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st } } free(dynamic_prefname); - LOG_LOCBACK(snode ? 1 : 0, 0); return rc; } -/** - * @brief Eat the node pointed by @p node_p by inserting it into @p parent and maintain the @p first_p pointing - * to the first child node. - * - * @param[in] parent Parent node to insert to, can be NULL in case of top-level (or provided first_p). - * @param[in,out] first_p Pointer to the first sibling node in case of top-level. - * @param[in,out] node_p pointer to the new node to insert, after the insert is done, pointer is set to NULL. - * @param[in] last If set, always insert at the end. - * @param[in] ext Extension instance of @p node_p, if any. - */ -static void -lydjson_maintain_children(struct lyd_node *parent, struct lyd_node **first_p, struct lyd_node **node_p, ly_bool last, - struct lysc_ext_instance *ext) -{ - if (!*node_p) { - return; - } - - /* insert, keep first pointer correct */ - if (ext) { - lyplg_ext_insert(parent, *node_p); - } else { - lyd_insert_node(parent, first_p, *node_p, last); - } - if (first_p) { - if (parent) { - *first_p = lyd_child(parent); - } else { - while ((*first_p)->prev->next) { - *first_p = (*first_p)->prev; - } - } - } - *node_p = NULL; -} - /** * @brief Wrapper for ::lyd_create_opaq(). * @@ -989,7 +945,7 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l dynamic = lydctx->jsonctx->dynamic; lydctx->jsonctx->dynamic = 0; - LY_CHECK_RET(lydjson_value_type_hint(lydctx, status_inner_p, &type_hint)); + LY_CHECK_RET(lydjson_value_type_hint(lydctx, NULL, status_inner_p, &type_hint)); } /* get the module name */ @@ -1010,9 +966,6 @@ lydjson_create_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_l return ret; } -static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, - struct ly_set *parsed); - /** * @brief Parse opaq node from the input. * @@ -1030,29 +983,30 @@ static LY_ERR lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *pa * @param[in,out] status_inner_p In case of processing JSON array, this parameter points to a standalone * context status of the array content. Otherwise, it is supposed to be the same as @p status_p. * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not. - * @param[out] node_p Pointer to the created opaq node. + * @param[out] node Pointer to the created opaq node. * @return LY_ERR value. */ static LY_ERR lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p, - struct lyd_node **first_p, struct lyd_node **node_p) + struct lyd_node **first_p, struct lyd_node **node) { LY_ERR ret = LY_SUCCESS; - LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup); + LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node), cleanup); - assert(*node_p); - LOG_LOCSET(NULL, *node_p); + /* insert */ + ret = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); + LY_CHECK_GOTO(ret, cleanup); if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) { /* special array null value */ - ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY; + ((struct lyd_node_opaq *)*node)->hints |= LYD_VALHINT_EMPTY; /* must be the only item */ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup); if (*status_inner_p != LYJSON_ARRAY_CLOSED) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member."); + LOGVAL(lydctx->jsonctx->ctx, *node, LYVE_SYNTAX, "Array \"null\" member with another member."); ret = LY_EVALID; goto cleanup; } @@ -1064,16 +1018,16 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le /* process another instance of the same node */ if (*status_inner_p == LYJSON_OBJECT) { /* array with objects, list */ - ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LIST; + ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LIST; /* but first process children of the object in the array */ do { - LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, (*node_p), NULL), cleanup); + LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL), cleanup); *status_inner_p = lyjson_ctx_status(lydctx->jsonctx); } while (*status_inner_p == LYJSON_OBJECT_NEXT); } else { /* array with values, leaf-list */ - ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST; + ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_LEAFLIST; } LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup); @@ -1084,35 +1038,28 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le /* continue with the next instance */ LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup); - assert(*node_p); - lydjson_maintain_children(parent, first_p, node_p, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, NULL); - - LOG_LOCBACK(0, 1); - - LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup); + LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node), + cleanup); - assert(*node_p); - LOG_LOCSET(NULL, *node_p); + /* insert */ + ret = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); + LY_CHECK_GOTO(ret, cleanup); } if (*status_p == LYJSON_OBJECT) { - ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_CONTAINER; + ((struct lyd_node_opaq *)*node)->hints |= LYD_NODEHINT_CONTAINER; /* process children */ do { - LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup); + LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL), cleanup); *status_p = lyjson_ctx_status(lydctx->jsonctx); } while (*status_p == LYJSON_OBJECT_NEXT); } finish: /* finish linking metadata */ - ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p)); + ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node)); cleanup: - if (*node_p) { - LOG_LOCBACK(0, 1); - } return ret; } @@ -1133,13 +1080,13 @@ lydjson_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_le * @param[in,out] status_p Pointer to the current status of the parser context, * since the function manipulates with the context and process the input, the status can be updated. * @param[in,out] first_p First top-level/parent sibling, must be set if @p parent is not. - * @param[out] node_p Pointer to the created opaq node. + * @param[out] node Pointer to the created opaq node. * @return LY_ERR value. */ static LY_ERR lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_t name_len, const char *prefix, size_t prefix_len, struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, - struct lyd_node **first_p, struct lyd_node **node_p) + struct lyd_node **first_p, struct lyd_node **node) { enum LYJSON_PARSER_STATUS status_inner = 0; @@ -1160,7 +1107,7 @@ lydjson_ctx_next_parse_opaq(struct lyd_json_ctx *lydctx, const char *name, size_ /* parse opaq node from the input */ LY_CHECK_RET(lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_p, &status_inner, - first_p, node_p)); + first_p, node)); return LY_SUCCESS; } @@ -1189,8 +1136,8 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, enum LYJSON_PARSER_STATUS *status_p, struct lyd_node **first_p, struct lyd_node **node_p) { LY_ERR r; - const char *opaq_name, *mod_name, *attr_mod; - size_t opaq_name_len, attr_mod_len; + const char *opaq_name, *mod_name, *attr_mod = NULL; + size_t opaq_name_len, attr_mod_len = 0; if (!attr_node) { /* learn the attribute module name */ @@ -1252,7 +1199,7 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, lydctx->parse_opts = prev_opts; LY_CHECK_RET(r); } else { - LY_CHECK_RET(lydjson_meta_attr(lydctx, snode, attr_node)); + LY_CHECK_RET(lydjson_meta_attr(lydctx, attr_node)); } return LY_SUCCESS; @@ -1265,6 +1212,8 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, * as before calling, despite it is necessary to process input data for checking. * @param[in] snode Schema node corresponding to the member currently being processed in the context. * @param[in] ext Extension instance of @p snode, if any. + * @param[in,out] parent Parent node, if any. + * @param[in,out] first_p First top-level node, is updated. * @param[in,out] status JSON parser status, is updated. * @param[out] node Parsed data (or opaque) node. * @return LY_SUCCESS if a node was successfully parsed, @@ -1272,16 +1221,14 @@ lydjson_parse_attribute(struct lyd_json_ctx *lydctx, struct lyd_node *attr_node, * @return LY_ERR on other errors. */ static LY_ERR -lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, - enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) +lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext, + struct lyd_node *parent, struct lyd_node **first_p, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t prev_parse_opts = lydctx->parse_opts, prev_int_opts = lydctx->int_opts; struct ly_in in_start; char *val = NULL; const char *end; - struct lyd_node *child = NULL; - ly_bool log_node = 0; assert(snode->nodetype & LYD_NODE_ANY); @@ -1299,36 +1246,9 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st /* create any node */ switch (*status) { case LYJSON_OBJECT: - /* create node */ - r = lyd_create_any(snode, NULL, LYD_ANYDATA_DATATREE, 1, node); + /* create empty node */ + r = lyd_create_any(snode, NULL, NULL, 0, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - - assert(*node); - LOG_LOCSET(NULL, *node); - log_node = 1; - - /* parse any data tree with correct options, first backup the current options and then make the parser - * process data as opaq nodes */ - lydctx->parse_opts &= ~LYD_PARSE_STRICT; - lydctx->parse_opts |= LYD_PARSE_OPAQ | (ext ? LYD_PARSE_ONLY : 0); - lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS; - lydctx->any_schema = snode; - - /* process the anydata content */ - do { - r = lydjson_subtree_r(lydctx, NULL, &child, NULL); - LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - - *status = lyjson_ctx_status(lydctx->jsonctx); - } while (*status == LYJSON_OBJECT_NEXT); - - /* finish linking metadata */ - r = lydjson_metadata_finish(lydctx, &child); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - - /* assign the data tree */ - ((struct lyd_node_any *)*node)->value.tree = child; - child = NULL; break; case LYJSON_ARRAY: /* skip until the array end */ @@ -1347,20 +1267,21 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st rc = LY_EMEM; goto cleanup; } - r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node); + r = lyd_create_any(snode, NULL, val, LYD_NODEHINT_LIST | LYD_NODEHINT_LEAFLIST, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); val = NULL; break; case LYJSON_STRING: /* string value */ if (lydctx->jsonctx->dynamic) { - LY_CHECK_GOTO(rc = lyd_create_any(snode, lydctx->jsonctx->value, LYD_ANYDATA_STRING, 1, node), cleanup); + LY_CHECK_GOTO(rc = lyd_create_any(snode, NULL, lydctx->jsonctx->value, + LYD_VALHINT_STRING | LYD_VALHINT_NUM64, 1, 0, node), cleanup); lydctx->jsonctx->dynamic = 0; } else { val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len); LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup); - r = lyd_create_any(snode, val, LYD_ANYDATA_STRING, 1, node); + r = lyd_create_any(snode, NULL, val, LYD_VALHINT_STRING | LYD_VALHINT_NUM64, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); val = NULL; } @@ -1373,13 +1294,13 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st val = strndup(lydctx->jsonctx->value, lydctx->jsonctx->value_len); LY_CHECK_ERR_GOTO(!val, LOGMEM(lydctx->jsonctx->ctx); rc = LY_EMEM, cleanup); - r = lyd_create_any(snode, val, LYD_ANYDATA_JSON, 1, node); + r = lyd_create_any(snode, NULL, val, (*status == LYJSON_NUMBER) ? LYD_VALHINT_DECNUM : LYD_VALHINT_BOOLEAN, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); val = NULL; break; case LYJSON_NULL: /* no value */ - r = lyd_create_any(snode, NULL, LYD_ANYDATA_JSON, 1, node); + r = lyd_create_any(snode, NULL, NULL, LYD_VALHINT_EMPTY, 1, 0, node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); break; default: @@ -1388,15 +1309,45 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st goto cleanup; } -cleanup: - if (log_node) { - LOG_LOCBACK(0, 1); + /* insert, needs LYD_EXT flag */ + if (ext) { + (*node)->flags |= LYD_EXT; + } + r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + + /* parse descendants */ + if (*status == LYJSON_OBJECT) { + if (lydctx->parse_opts & LYD_PARSE_ANYDATA_STRICT) { + /* explicit strict data parsing */ + lydctx->parse_opts |= LYD_PARSE_STRICT; + } else { + /* make the parser process data as opaq nodes */ + lydctx->parse_opts &= ~LYD_PARSE_STRICT; + lydctx->parse_opts |= LYD_PARSE_OPAQ; + } + lydctx->parse_opts |= (ext ? LYD_PARSE_ONLY : 0); + lydctx->int_opts |= LYD_INTOPT_ANY | LYD_INTOPT_WITH_SIBLINGS; + lydctx->any_schema = snode; + + /* process the anydata content */ + do { + r = lydjson_subtree_r(lydctx, *node, &((struct lyd_node_any *)*node)->child, NULL); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + *status = lyjson_ctx_status(lydctx->jsonctx); + } while (*status == LYJSON_OBJECT_NEXT); + + /* finish linking metadata */ + r = lydjson_metadata_finish(lydctx, &((struct lyd_node_any *)*node)->child); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); } + +cleanup: lydctx->parse_opts = prev_parse_opts; lydctx->int_opts = prev_int_opts; lydctx->any_schema = NULL; free(val); - lyd_free_tree(child); return rc; } @@ -1406,6 +1357,8 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st * @param[in] lydctx JSON data parser context. * @param[in] snode Schema node corresponding to the member currently being processed in the context. * @param[in] ext Extension instance of @p snode, if any. + * @param[in,out] parent Parent node, if any. + * @param[in,out] first_p First top-level node, is updated. * @param[in,out] status JSON parser status, is updated. * @param[out] node Parsed data (or opaque) node. * @return LY_SUCCESS if a node was successfully parsed, @@ -1413,8 +1366,8 @@ lydjson_parse_any(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, st * @return LY_ERR on other errors. */ static LY_ERR -lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, struct lysc_ext_instance *ext, - enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) +lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node *snode, const struct lysc_ext_instance *ext, + struct lyd_node *parent, struct lyd_node **first_p, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t prev_parse_opts = lydctx->parse_opts; @@ -1424,8 +1377,12 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node /* create inner node */ LY_CHECK_RET(lyd_create_inner(snode, node)); - /* use it for logging */ - LOG_LOCSET(NULL, *node); + /* insert, needs LYD_EXT flag */ + if (ext) { + (*node)->flags |= LYD_EXT; + } + r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (ext) { /* only parse these extension data and validate afterwards */ @@ -1437,6 +1394,10 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node r = lydjson_subtree_r(lydctx, *node, lyd_node_child_p(*node), NULL); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + /* insert again, node may be a list that had its keys missing */ + r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + *status = lyjson_ctx_status(lydctx->jsonctx); } while (*status == LYJSON_OBJECT_NEXT); @@ -1458,11 +1419,8 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node cleanup: lydctx->parse_opts = prev_parse_opts; - LOG_LOCBACK(0, 1); - if (!(*node)->hash) { - /* list without keys is unusable */ - lyd_free_tree(*node); - *node = NULL; + if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { + lyd_parser_node_free(first_p, node); } return rc; } @@ -1488,14 +1446,12 @@ lydjson_parse_instance_inner(struct lyd_json_ctx *lydctx, const struct lysc_node */ static LY_ERR lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct lyd_node **first_p, - const struct lysc_node *snode, struct lysc_ext_instance *ext, const char *name, size_t name_len, + const struct lysc_node *snode, const struct lysc_ext_instance *ext, const char *name, size_t name_len, const char *prefix, size_t prefix_len, enum LYJSON_PARSER_STATUS *status, struct lyd_node **node) { LY_ERR r, rc = LY_SUCCESS; uint32_t type_hints = 0; - LOG_LOCSET(snode, NULL); - r = lydjson_data_check_opaq(lydctx, snode, &type_hints); if (r == LY_SUCCESS) { assert(snode->nodetype & (LYD_NODE_TERM | LYD_NODE_INNER | LYD_NODE_ANY)); @@ -1510,10 +1466,17 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str } /* create terminal node */ - r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, lydctx->jsonctx->value, - lydctx->jsonctx->value_len, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node); + r = lyd_parser_create_term((struct lyd_ctx *)lydctx, snode, parent, lydctx->jsonctx->value, + lydctx->jsonctx->value_len * 8, &lydctx->jsonctx->dynamic, LY_VALUE_JSON, NULL, type_hints, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + /* insert, needs LYD_EXT flag */ + if (ext) { + (*node)->flags |= LYD_EXT; + } + r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, *node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + /* move JSON parser */ if (*status == LYJSON_ARRAY) { /* only [null], 2 more moves are needed */ @@ -1527,11 +1490,11 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str } } else if (snode->nodetype & LYD_NODE_INNER) { /* create inner node */ - r = lydjson_parse_instance_inner(lydctx, snode, ext, status, node); + r = lydjson_parse_instance_inner(lydctx, snode, ext, parent, first_p, status, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } else { /* create any node */ - r = lydjson_parse_any(lydctx, snode, ext, status, node); + r = lydjson_parse_any(lydctx, snode, ext, parent, first_p, status, node); LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } LY_CHECK_GOTO(!*node, cleanup); @@ -1539,12 +1502,6 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str /* add/correct flags */ r = lyd_parser_set_data_flags(*node, &(*node)->meta, (struct lyd_ctx *)lydctx, ext); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - - if (!(lydctx->parse_opts & LYD_PARSE_ONLY)) { - /* store for ext instance node validation, if needed */ - r = lyd_validate_node_ext(*node, &lydctx->ext_node); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - } } else if (r == LY_ENOT) { /* parse it again as an opaq node */ r = lydjson_parse_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status, status, first_p, node); @@ -1562,7 +1519,6 @@ lydjson_parse_instance(struct lyd_json_ctx *lydctx, struct lyd_node *parent, str } cleanup: - LOG_LOCBACK(1, 0); return rc; } @@ -1582,7 +1538,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l enum LYJSON_PARSER_STATUS status = lyjson_ctx_status(lydctx->jsonctx); const char *name, *prefix = NULL, *expected = NULL; size_t name_len, prefix_len = 0; - ly_bool is_meta = 0, parse_subtree; + ly_bool is_meta = 0; const struct lysc_node *snode = NULL; struct lysc_ext_instance *ext = NULL; struct lyd_node *node = NULL, *attr_node = NULL; @@ -1592,10 +1548,6 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l assert(parent || first_p); assert((status == LYJSON_OBJECT) || (status == LYJSON_OBJECT_NEXT)); - parse_subtree = lydctx->parse_opts & LYD_PARSE_SUBTREE ? 1 : 0; - /* all descendants should be parsed */ - lydctx->parse_opts &= ~LYD_PARSE_SUBTREE; - r = lyjson_ctx_next(lydctx->jsonctx, &status); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (status == LYJSON_OBJECT_CLOSED) { @@ -1615,8 +1567,8 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (status != LYJSON_STRING) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_STRING), - lyjson_token2str(status)); + LOGVAL(lydctx->jsonctx->ctx, NULL, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", + lyjson_token2str(LYJSON_STRING), lyjson_token2str(status)); rc = LY_EVALID; goto cleanup; } @@ -1626,6 +1578,10 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l lydctx->jsonctx->value, lydctx->jsonctx->value_len, NULL, LY_VALUE_JSON, NULL, LYD_VALHINT_STRING, &node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + /* insert */ + r = lyd_parser_node_insert(parent, first_p, NULL, lydctx->parse_opts, node); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + /* validate the value */ r = lyd_parser_notif_eventtime_validate(node); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); @@ -1657,7 +1613,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l if (is_meta) { /* parse as metadata */ if (!name_len && !prefix_len && !parent) { - LOGVAL(ctx, LYVE_SYNTAX_JSON, + LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Invalid metadata format - \"@\" can be used only inside anydata, container or list entries."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); @@ -1679,7 +1635,7 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l /* opaq node cannot have an empty string as the name. */ if (name_len == 0) { - LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX_JSON, "JSON object member name cannot be a zero-length string."); + LOGVAL(lydctx->jsonctx->ctx, parent, LYVE_SYNTAX_JSON, "JSON object member name cannot be a zero-length string."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -1749,9 +1705,6 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l } LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - lydjson_maintain_children(parent, first_p, &node, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); - /* move after the item(s) */ r = lyjson_ctx_next(lydctx->jsonctx, &status); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); @@ -1787,21 +1740,15 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l ly_set_add(parsed, node, 1, NULL); } - /* finally connect the parsed node, is zeroed */ - lydjson_maintain_children(parent, first_p, &node, - lydctx->parse_opts & LYD_PARSE_ORDERED ? LYD_INSERT_NODE_LAST : LYD_INSERT_NODE_DEFAULT, ext); - - if (!parse_subtree) { - /* move after the item(s) */ - r = lyjson_ctx_next(lydctx->jsonctx, &status); - LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - } + /* move after the item(s) */ + r = lyjson_ctx_next(lydctx->jsonctx, &status); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); /* success */ goto cleanup; representation_error: - LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as name/%s.", + LOGVAL(ctx, parent, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as name/%s.", expected, lys_nodetype2str(snode->nodetype), snode->name, lyjson_token2str(status)); rc = LY_EVALID; if (lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) { @@ -1813,7 +1760,9 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l cleanup: free(value); - lyd_free_tree(node); + if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { + lyd_parser_node_free(first_p, &node); + } return rc; } @@ -1821,15 +1770,17 @@ lydjson_subtree_r(struct lyd_json_ctx *lydctx, struct lyd_node *parent, struct l * @brief Common start of JSON parser processing different types of the input data. * * @param[in] ctx libyang context + * @param[in] schema Schema node of the potential bare value to check. * @param[in] in Input structure. * @param[in] parse_opts Options for parser, see @ref dataparseroptions. * @param[in] val_opts Options for the validation phase, see @ref datavalidationoptions. + * @param[out] status_p Initial status of the input structure. * @param[out] lydctx_p Data parser context to finish validation. * @return LY_ERR value. */ static LY_ERR -lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, - struct lyd_json_ctx **lydctx_p) +lyd_parse_json_init(const struct ly_ctx *ctx, const struct lysc_node *schema, struct ly_in *in, uint32_t parse_opts, + uint32_t val_opts, enum LYJSON_PARSER_STATUS *status_p, struct lyd_json_ctx **lydctx_p) { LY_ERR ret = LY_SUCCESS; struct lyd_json_ctx *lydctx; @@ -1848,55 +1799,169 @@ lyd_parse_json_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_o status = lyjson_ctx_status(lydctx->jsonctx); /* parse_opts & LYD_PARSE_SUBTREE not implemented */ - if (status != LYJSON_OBJECT) { - /* expecting top-level object */ - LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expected top-level JSON object, but %s found.", lyjson_token2str(status)); + /* there are two options: either we want to parse a bare JSON value or a JSON object + * node of the bare JSON value has to have a schema, otherwise we do not know where to put the value + * the only types of nodes that can take on a value are a leaf (number, string or bool) and a leaf-list (array) */ + if (schema && + (((status == LYJSON_ARRAY) && (schema->nodetype & LYS_LEAFLIST)) || + (((status == LYJSON_NUMBER) || (status == LYJSON_STRING) || (status == LYJSON_FALSE) || + (status == LYJSON_TRUE) || (status == LYJSON_NULL) || (status == LYJSON_ARRAY)) && (schema->nodetype & LYS_LEAF)))) { + /* bare value (bare anydata 'value = object' is not supported) */ + } else if (status == LYJSON_OBJECT) { + /* JSON object */ + } else { + /* expecting top-level object or bare value */ + LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Expected top-level JSON object or correct bare value, but %s found.", + lyjson_token2str(status)); *lydctx_p = NULL; lyd_json_ctx_free((struct lyd_ctx *)lydctx); return LY_EVALID; } *lydctx_p = lydctx; + if (status_p) { + *status_p = status; + } return LY_SUCCESS; } +/** + * @brief Parse a bare JSON value. + * + * @param[in] lydctx Data parser context. + * @param[in,out] status Current status of the data parser context. + * @param[in] parent Parent to connect the parsed nodes to, if any. + * @param[in] schema Optional schema node of the parsed node (mandatory when parsing JSON value fragment). + * @param[in,out] first_p Pointer to the first top-level parsed node, used only if @p parent is NULL. + * @return LY_ERR value. + */ +static LY_ERR +lyd_parse_json_bare_value(struct lyd_json_ctx *lydctx, enum LYJSON_PARSER_STATUS *status, struct lyd_node *parent, + const struct lysc_node *schema, struct lyd_node **first_p) +{ + LY_ERR r, rc = LY_SUCCESS; + const struct ly_ctx *ctx = lydctx->jsonctx->ctx; + struct lyd_node *node = NULL; + const char *expected = NULL; + + assert(schema); + + /* this branch partly copies the behavior of lydjson_subtree_r() */ + + /* set expected representation */ + switch (schema->nodetype) { + case LYS_LEAFLIST: + expected = "array of values"; + break; + case LYS_LEAF: + if (*status == LYJSON_ARRAY) { + expected = "[null]"; + } else { + expected = "value"; + } + break; + } + + /* check the representation according to the nodetype and then continue with the content */ + /* for now object values are not supported (anydata) */ + /* for now extensions not supported */ + switch (schema->nodetype) { + case LYS_LEAFLIST: + LY_CHECK_GOTO(*status != LYJSON_ARRAY, representation_error); + + /* process all the values/objects */ + do { + /* move into array/next value */ + r = lyjson_ctx_next(lydctx->jsonctx, status); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + if (*status == LYJSON_ARRAY_CLOSED) { + /* empty array, fine... */ + break; + } + + r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name), + NULL, 0, status, &node); + if (r == LY_ENOT) { + goto representation_error; + } + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + /* move after the item(s) */ + r = lyjson_ctx_next(lydctx->jsonctx, status); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } while (*status == LYJSON_ARRAY_NEXT); + + break; + case LYS_LEAF: + /* process the value/object */ + r = lydjson_parse_instance(lydctx, parent, first_p, schema, NULL, schema->name, strlen(schema->name), + NULL, 0, status, &node); + if (r == LY_ENOT) { + goto representation_error; + } + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + + break; + } + + goto cleanup; + +representation_error: + LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s \"%s\" is represented in input data as %s.", + expected, lys_nodetype2str(schema->nodetype), schema->name, lyjson_token2str(*status)); + rc = LY_EVALID; + +cleanup: + if (rc && (!(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { + lyd_parser_node_free(first_p, &node); + } + return rc; +} + LY_ERR -lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, +lyd_parse_json(const struct ly_ctx *ctx, struct lyd_node *parent, const struct lysc_node *schema, struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts, - struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p) + struct ly_set *parsed, struct lyd_ctx **lydctx_p) { LY_ERR r, rc = LY_SUCCESS; struct lyd_json_ctx *lydctx = NULL; - enum LYJSON_PARSER_STATUS status; + enum LYJSON_PARSER_STATUS status = LYJSON_ERROR; - rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx); + rc = lyd_parse_json_init(ctx, schema, in, parse_opts, val_opts, &status, &lydctx); LY_CHECK_GOTO(rc, cleanup); lydctx->int_opts = int_opts; - lydctx->ext = ext; /* find the operation node if it exists already */ LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup); - /* read subtree(s) */ - do { - r = lydjson_subtree_r(lydctx, parent, first_p, parsed); - LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); + if (status != LYJSON_OBJECT) { + /* parse bare JSON value */ + r = lyd_parse_json_bare_value(lydctx, &status, parent, schema, first_p); + LY_CHECK_ERR_GOTO(r, rc = r, cleanup); + } else { + /* parse JSON object */ - status = lyjson_ctx_status(lydctx->jsonctx); + /* read subtree(s) */ + do { + r = lydjson_subtree_r(lydctx, parent, first_p, parsed); + LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); - if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) { - break; - } - } while (status == LYJSON_OBJECT_NEXT); + status = lyjson_ctx_status(lydctx->jsonctx); + + if (!(int_opts & LYD_INTOPT_WITH_SIBLINGS)) { + break; + } + } while (status == LYJSON_OBJECT_NEXT); + } if ((int_opts & LYD_INTOPT_NO_SIBLINGS) && lydctx->jsonctx->in->current[0] && (status != LYJSON_OBJECT_CLOSED)) { - LOGVAL(ctx, LYVE_SYNTAX, "Unexpected sibling node."); + LOGVAL(ctx, NULL, LYVE_SYNTAX, "Unexpected sibling node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { - LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); + LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -1905,19 +1970,6 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st r = lydjson_metadata_finish(lydctx, parent ? lyd_node_child_p(parent) : first_p); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); - if (parse_opts & LYD_PARSE_SUBTREE) { - /* check for a sibling object */ - assert(subtree_sibling); - if (status == LYJSON_OBJECT_NEXT) { - *subtree_sibling = 1; - - /* move to the next object */ - ly_in_skip(lydctx->jsonctx->in, 1); - } else { - *subtree_sibling = 0; - } - } - cleanup: /* there should be no unresolved types stored */ assert(!(parse_opts & LYD_PARSE_ONLY) || !lydctx || (!lydctx->node_types.count && !lydctx->meta_types.count && @@ -1926,11 +1978,16 @@ lyd_parse_json(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) { lyd_json_ctx_free((struct lyd_ctx *)lydctx); } else { - *lydctx_p = (struct lyd_ctx *)lydctx; - /* the JSON context is no more needed, freeing it also stops logging line numbers which would be confusing now */ lyjson_ctx_free(lydctx->jsonctx); lydctx->jsonctx = NULL; + + /* set optional lydctx pointer, otherwise free */ + if (lydctx_p) { + *lydctx_p = (struct lyd_ctx *)lydctx; + } else { + lyd_json_ctx_free((struct lyd_ctx *)lydctx); + } } return rc; } @@ -1962,7 +2019,7 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul r = lyjson_ctx_next(jsonctx, &status); LY_CHECK_ERR_GOTO(r, rc = r, cleanup); if (status == LYJSON_OBJECT_CLOSED) { - LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON object."); + LOGVAL(jsonctx->ctx, NULL, LYVE_SYNTAX, "Empty JSON object."); rc = LY_EVALID; goto cleanup; } @@ -1971,15 +2028,16 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul assert(status == LYJSON_OBJECT_NAME); lydjson_parse_name(jsonctx->value, jsonctx->value_len, &nam, &nam_len, &prefix, &prefix_len, &is_meta); if (is_meta) { - LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected metadata."); + LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected metadata."); rc = LY_EVALID; goto cleanup; } else if (module && ly_strncmp(module, prefix, prefix_len)) { - LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, module); + LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected module \"%.*s\" instead of \"%s\".", (int)prefix_len, prefix, + module); rc = LY_EVALID; goto cleanup; } else if (ly_strncmp(name, nam, nam_len)) { - LOGVAL(jsonctx->ctx, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name); + LOGVAL(jsonctx->ctx, NULL, LYVE_DATA, "Unexpected object \"%.*s\" instead of \"%s\".", (int)nam_len, nam, name); rc = LY_EVALID; goto cleanup; } @@ -2001,9 +2059,9 @@ lydjson_envelope(struct lyjson_ctx *jsonctx, const char *name, const char *modul } LY_ERR -lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, struct lyd_node *parent, - struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, - struct lyd_node **envp, struct ly_set *parsed, struct lyd_ctx **lydctx_p) +lyd_parse_json_restconf(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **first_p, struct ly_in *in, + uint32_t parse_opts, uint32_t val_opts, enum lyd_type data_type, struct lyd_node **envp, struct ly_set *parsed, + struct lyd_ctx **lydctx_p) { LY_ERR rc = LY_SUCCESS, r; struct lyd_json_ctx *lydctx = NULL; @@ -2016,12 +2074,10 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance assert((data_type == LYD_TYPE_RPC_RESTCONF) || (data_type == LYD_TYPE_NOTIF_RESTCONF) || (data_type == LYD_TYPE_REPLY_RESTCONF)); - assert(!(parse_opts & LYD_PARSE_SUBTREE)); /* init context */ - rc = lyd_parse_json_init(ctx, in, parse_opts, val_opts, &lydctx); + rc = lyd_parse_json_init(ctx, NULL, in, parse_opts, val_opts, NULL, &lydctx); LY_CHECK_GOTO(rc, cleanup); - lydctx->ext = ext; switch (data_type) { case LYD_TYPE_RPC_RESTCONF: @@ -2075,7 +2131,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance /* close all opened elements */ for (i = 0; i < close_elem; ++i) { if (lyjson_ctx_status(lydctx->jsonctx) != LYJSON_OBJECT_CLOSED) { - LOGVAL(ctx, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED), + LOGVAL(ctx, NULL, LYVE_SYNTAX_JSON, "Expecting JSON %s but %s found.", lyjson_token2str(LYJSON_OBJECT_CLOSED), lyjson_token2str(lyjson_ctx_status(lydctx->jsonctx))); rc = LY_EVALID; goto cleanup; @@ -2086,7 +2142,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance } if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) && !lydctx->op_node) { - LOGVAL(ctx, LYVE_DATA, "Missing the operation node."); + LOGVAL(ctx, NULL, LYVE_DATA, "Missing the operation node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } @@ -2094,7 +2150,7 @@ lyd_parse_json_restconf(const struct ly_ctx *ctx, const struct lysc_ext_instance /* parse as a child of the envelope */ node = (*first_p)->prev; if (node->schema) { - LOGVAL(ctx, LYVE_DATA, "Missing notification \"eventTime\" node."); + LOGVAL(ctx, NULL, LYVE_DATA, "Missing notification \"eventTime\" node."); r = LY_EVALID; LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup); } else { diff --git a/src/printer_cbor.c b/src/printer_cbor.c index dc4fc2a65..4ab8ee1e9 100644 --- a/src/printer_cbor.c +++ b/src/printer_cbor.c @@ -14,8 +14,6 @@ #define _GNU_SOURCE -#ifdef ENABLE_CBOR_SUPPORT - #include #include #include @@ -28,6 +26,7 @@ #include "out_internal.h" #include "parser_data.h" #include "plugins_exts/metadata.h" +#include "plugins_internal.h" #include "plugins_types.h" #include "printer_data.h" #include "printer_internal.h" @@ -35,7 +34,7 @@ #include "tree.h" #include "tree_data.h" #include "tree_schema.h" -#include "cbor.h" +#include "lcbor.h" /** * @brief CBOR printer context. @@ -49,8 +48,9 @@ struct cborpr_ctx { struct ly_set open; /**< currently open array(s) */ const struct lyd_node *first_leaflist; /**< first printed leaf-list instance, used when printing its metadata/attributes */ - + cbor_item_t *root_map; /**< root CBOR map */ + cbor_item_t *array; /**< currently open CBOR array for leaf-list/list instances */ }; static LY_ERR cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map); @@ -172,7 +172,7 @@ node_prefix(const struct lyd_node *node) * @return 0 in case the nodes' modules are the same * @return 1 in case the nodes belongs to different modules */ -int +static int cbor_nscmp(const struct lyd_node *node1, const struct lyd_node *node2) { assert(node1 || node2); @@ -275,7 +275,8 @@ cbor_print_member_name2(struct cborpr_ctx *pctx, const struct lyd_node *parent, } default: /* cannot be created */ - LOGINT_RET(pctx->ctx); + LOGINT(pctx->ctx); + return NULL; } name_str = name->name; @@ -326,7 +327,7 @@ cbor_print_value(struct cborpr_ctx *pctx, const struct ly_ctx *ctx, const struct const char *value; cbor_item_t *item = NULL; - value = val->realtype->plugin->print(ctx, val, LY_VALUE_JSON, (void *)local_mod, &dynamic, NULL); + value = LYSC_GET_TYPE_PLG(val->realtype->plugin_ref)->print(ctx, val, LY_VALUE_CBOR, (void *)local_mod, &dynamic, NULL); LY_CHECK_RET(!value, LY_EINVAL); basetype = val->realtype->basetype; @@ -699,34 +700,16 @@ cbor_print_any_content(struct cborpr_ctx *pctx, struct lyd_node_any *any, cbor_i LY_ERR ret = LY_SUCCESS; struct lyd_node *iter; const struct lyd_node *prev_parent; - uint32_t prev_opts, *prev_lo, temp_lo = 0; + uint32_t prev_opts; cbor_item_t *content_map = NULL; assert(any->schema->nodetype & LYD_NODE_ANY); - if ((any->schema->nodetype == LYS_ANYDATA) && (any->value_type != LYD_ANYDATA_DATATREE)) { + if ((any->schema->nodetype == LYS_ANYDATA) && any->value) { LOGINT_RET(pctx->ctx); } - if (any->value_type == LYD_ANYDATA_LYB) { - uint32_t parser_options = LYD_PARSE_ONLY | LYD_PARSE_OPAQ | LYD_PARSE_STRICT; - - /* turn logging off */ - prev_lo = ly_temp_log_options(&temp_lo); - - /* try to parse it into a data tree */ - if (lyd_parse_data_mem(pctx->ctx, any->value.mem, LYD_LYB, parser_options, 0, &iter) == LY_SUCCESS) { - /* successfully parsed */ - free(any->value.mem); - any->value.tree = iter; - any->value_type = LYD_ANYDATA_DATATREE; - } - - /* turn logging on again */ - ly_temp_log_options(prev_lo); - } - switch (any->value_type) { - case LYD_ANYDATA_DATATREE: + if (any->child) { /* create a map for the content */ content_map = cbor_new_indefinite_map(); LY_CHECK_RET(!content_map, LY_EMEM); @@ -735,8 +718,8 @@ cbor_print_any_content(struct cborpr_ctx *pctx, struct lyd_node_any *any, cbor_i prev_parent = pctx->parent; prev_opts = pctx->options; pctx->parent = &any->node; - pctx->options &= ~LYD_PRINT_WITHSIBLINGS; - LY_LIST_FOR(any->value.tree, iter) { + pctx->options &= ~LYD_PRINT_SIBLINGS; + LY_LIST_FOR(any->child, iter) { ret = cbor_print_node(pctx, iter, content_map); LY_CHECK_ERR_RET(ret, cbor_decref(&content_map), ret); } @@ -744,39 +727,16 @@ cbor_print_any_content(struct cborpr_ctx *pctx, struct lyd_node_any *any, cbor_i pctx->options = prev_opts; *item_p = content_map; - break; - case LYD_ANYDATA_JSON: - if (!any->value.json) { - /* no content */ - if (any->schema->nodetype == LYS_ANYXML) { - *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); - } else { - *item_p = cbor_new_indefinite_map(); - } - } else { - /* JSON content - store as string */ - *item_p = cbor_build_string(any->value.json); - } - break; - case LYD_ANYDATA_STRING: - case LYD_ANYDATA_XML: - if (!any->value.str) { - /* no content */ - if (any->schema->nodetype == LYS_ANYXML) { - *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); - } else { - *item_p = cbor_new_indefinite_map(); - } + } else if (any->value) { + /* print as a string */ + *item_p = cbor_build_string(any->value); + } else { + /* no content */ + if (any->schema->nodetype == LYS_ANYXML) { + *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); } else { - /* print as a string */ - *item_p = cbor_build_string(any->value.str); + *item_p = cbor_new_indefinite_map(); } - break; - case LYD_ANYDATA_LYB: - /* LYB format is not supported */ - LOGWRN(pctx->ctx, "Unable to print anydata content (type %d) as CBOR.", any->value_type); - *item_p = cbor_build_ctrl(CBOR_CTRL_NULL); - break; } return LY_SUCCESS; @@ -904,7 +864,7 @@ cbor_print_array_is_last_inst(struct cborpr_ctx *pctx, const struct lyd_node *no return 0; } - if ((pctx->root == node) && !(pctx->options & LYD_PRINT_WITHSIBLINGS)) { + if ((pctx->root == node) && !(pctx->options & LYD_PRINT_SIBLINGS)) { /* the only printed instance */ return 1; } @@ -1239,7 +1199,6 @@ cbor_print_opaq(struct cborpr_ctx *pctx, const struct lyd_node_opaq *node, cbor_ static LY_ERR cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_t *parent_map) { - static cbor_item_t *array = NULL; if (!lyd_node_should_print(node, pctx->options)) { if (cbor_print_array_is_last_inst(pctx, node)) { @@ -1249,7 +1208,7 @@ cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_ } if (!node->schema) { - LY_CHECK_RET(cbor_print_opaq(pctx, (const struct lyd_node_opaq *)node, parent_map, &array)); + LY_CHECK_RET(cbor_print_opaq(pctx, (const struct lyd_node_opaq *)node, parent_map, &pctx->array)); } else { switch (node->schema->nodetype) { case LYS_RPC: @@ -1263,7 +1222,7 @@ cbor_print_node(struct cborpr_ctx *pctx, const struct lyd_node *node, cbor_item_ break; case LYS_LEAFLIST: case LYS_LIST: - LY_CHECK_RET(cbor_print_leaf_list(pctx, node, parent_map, &array)); + LY_CHECK_RET(cbor_print_leaf_list(pctx, node, parent_map, &pctx->array)); break; case LYS_ANYDATA: case LYS_ANYXML: @@ -1323,7 +1282,7 @@ cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option pctx.root = node; LY_CHECK_ERR_RET(cbor_print_node(&pctx, node, pctx.root_map), cbor_decref(&pctx.root_map); ly_set_erase(&pctx.open, NULL), LY_EINVAL); - if (!(options & LYD_PRINT_WITHSIBLINGS)) { + if (!(options & LYD_PRINT_SIBLINGS)) { break; } } @@ -1347,5 +1306,3 @@ cbor_print_data(struct ly_out *out, const struct lyd_node *root, uint32_t option ly_print_flush(out); return LY_SUCCESS; } - -#endif /* ENABLE_CBOR_SUPPORT */ \ No newline at end of file diff --git a/src/printer_data.c b/src/printer_data.c index cb1611a4b..ceee558f0 100644 --- a/src/printer_data.c +++ b/src/printer_data.c @@ -38,9 +38,11 @@ lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, u case LYD_LYB: ret = lyb_print_data(out, root, options); break; +#ifdef ENABLE_CBOR_SUPPORT case LYD_CBOR: ret = cbor_print_data(out, root, options); break; +#endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "Unknown data output format."); ret = LY_EINVAL; diff --git a/src/tree_data.c b/src/tree_data.c index d87e389e3..61069d189 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -91,7 +91,6 @@ lyd_parse_get_format(const struct ly_in *in, LYD_FORMAT format) * @brief Parse YANG data into a data tree. * * @param[in] ctx libyang context. - * @param[in] ext Optional extension instance to parse data following the schema tree specified in the extension instance * @param[in] parent Parent to connect the parsed nodes to, if any. * @param[in,out] first_p Pointer to the first parsed node. * @param[in] in Input handle to read the input from. @@ -135,10 +134,11 @@ lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **f case LYD_LYB: r = lyd_parse_lyb(ctx, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, &lydctx); break; +#ifdef ENABLE_CBOR_SUPPORT case LYD_CBOR: - r = lyd_parse_cbor(ctx, ext, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, - &subtree_sibling, &lydctx); + r = lyd_parse_cbor(ctx, NULL, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); break; +#endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGARG(ctx, format); r = LY_EINVAL; @@ -484,9 +484,11 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in case LYD_LYB: rc = lyd_parse_lyb(ctx, parent, &first, in, parse_options, val_opts, int_opts, &parsed, &lydctx); break; +#ifdef ENABLE_CBOR_SUPPORT case LYD_CBOR: - rc = lyd_parse_cbor(ctx, ext, parent, &first, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); + rc = lyd_parse_cbor(ctx, NULL, parent, &first, in, parse_options, val_opts, int_opts, &parsed, NULL, &lydctx); break; +#endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGARG(ctx, format); rc = LY_EINVAL; From 46af93063961c4abf31da98c1b9cba31c1d21ed0 Mon Sep 17 00:00:00 2001 From: Meher C Date: Tue, 17 Mar 2026 22:31:22 +0000 Subject: [PATCH 8/8] fix: address PR review and formatting changes - Fix copyright years in all new files - Fix function declaration style and moving doxygen comments to the right place - Fixing other small formating errors and removing dead code --- CMakeLists.txt | 1 - src/lcbor.c | 24 ++++++------------------ src/lcbor.h | 22 ++++++++++------------ src/parser_cbor.c | 2 +- src/printer_cbor.c | 2 +- src/printer_data.c | 5 +++++ src/tree_data.c | 10 ++++++++++ 7 files changed, 33 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 971c730c0..056e40d2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,7 +326,6 @@ endif() find_package(CBOR) if(CBOR_FOUND) - set(CBOR_SUPPORT ON) list(APPEND libsrc src/parser_cbor.c src/lcbor.c src/printer_cbor.c) list(APPEND headers src/lcbor.h) list(APPEND format_sources src/parser_cbor.c src/lcbor.h src/lcbor.c src/printer_cbor.c) diff --git a/src/lcbor.c b/src/lcbor.c index 03ea9ba59..d0431fa8a 100644 --- a/src/lcbor.c +++ b/src/lcbor.c @@ -3,7 +3,7 @@ * @author MeherRushi * @brief CBOR data parser for libyang (abstraction over libcbor) * - * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2026 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. @@ -46,12 +46,8 @@ lycbor_token2str(enum cbor_type cbortype) return ""; } -/** - * @brief Free CBOR context. - * - * @param[in] cborctx CBOR context to free. - */ -void lycbor_ctx_free(struct lycbor_ctx *cborctx) +void +lycbor_ctx_free(struct lycbor_ctx *cborctx) { if (cborctx) { if (cborctx->cbor_data) { @@ -78,14 +74,6 @@ lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format) return LY_SUCCESS; } -/** - * @brief Create new CBOR context for parsing. - * - * @param[in] ctx libyang context. - * @param[in] in Input handler. - * @param[out] cborctx_p Pointer to store the created CBOR context. - * @return LY_ERR value. - */ LY_ERR lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p) { @@ -107,6 +95,9 @@ lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **c cborctx->in = in; cborctx->format = format; + /* input line logging */ + ly_log_location(NULL, NULL, in); + /* load and parse CBOR data */ cborctx->cbor_data = cbor_load((cbor_data)in->current, in->length, &result); if (!cborctx->cbor_data) { @@ -121,9 +112,6 @@ lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **c return LY_EVALID; } - /* input line logging */ - ly_log_location(NULL, NULL, in); - *cborctx_p = cborctx; return ret; } diff --git a/src/lcbor.h b/src/lcbor.h index ab1eba52e..4f7a9d8d4 100644 --- a/src/lcbor.h +++ b/src/lcbor.h @@ -3,7 +3,7 @@ * @author MeherRushi * @brief CBOR data parser routines for libyang (abstraction over libcbor) * - * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2026 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. @@ -37,13 +37,13 @@ enum lyd_cbor_format }; struct lycbor_ctx { - const struct ly_ctx *ctx; /**< libyang context */ - struct ly_in *in; /**< input structure */ - + const struct ly_ctx *ctx; + struct ly_in *in; /** input structure */ + cbor_item_t *cbor_data; /**< parsed CBOR data */ - + enum lyd_cbor_format format; /**< CBOR format variant */ - + struct { cbor_item_t *cbor_data; /**< parsed CBOR data */ enum lyd_cbor_format format; /**< CBOR format variant */ @@ -63,19 +63,17 @@ const char *lycbor_token2str(enum cbor_type cbortype); * @brief Create new CBOR context for parsing. * * @param[in] ctx libyang context. - * @param[in] in Input handler. - * @param[out] cborctx_p Pointer to store the created CBOR context. + * @param[in] in CBOR string data to parse. + * @param[out] cborctx New CBOR parser context containing parsed CBOR data. * @return LY_ERR value. */ -LY_ERR -lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p); +LY_ERR lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx); /** * @brief Free CBOR context. * * @param[in] cborctx CBOR context to free. */ -void -lycbor_ctx_free(struct lycbor_ctx *cborctx); +void lycbor_ctx_free(struct lycbor_ctx *cborctx); #endif /* LY_CBOR_H_ */ diff --git a/src/parser_cbor.c b/src/parser_cbor.c index 9753c5017..a816d8179 100644 --- a/src/parser_cbor.c +++ b/src/parser_cbor.c @@ -3,7 +3,7 @@ * @author Meher Rushi * @brief CBOR data parser for libyang * - * Copyright (c) 2020 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2026 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. diff --git a/src/printer_cbor.c b/src/printer_cbor.c index 4ab8ee1e9..cde213083 100644 --- a/src/printer_cbor.c +++ b/src/printer_cbor.c @@ -3,7 +3,7 @@ * @author Meher Rushi * @brief CBOR printer for libyang data structure * - * Copyright (c) 2015 - 2023 CESNET, z.s.p.o. + * Copyright (c) 2026 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. diff --git a/src/printer_data.c b/src/printer_data.c index ceee558f0..0fd1775bb 100644 --- a/src/printer_data.c +++ b/src/printer_data.c @@ -42,6 +42,11 @@ lyd_print_(struct ly_out *out, const struct lyd_node *root, LYD_FORMAT format, u case LYD_CBOR: ret = cbor_print_data(out, root, options); break; +#else + case LYD_CBOR: + LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "CBOR format not supported, libcbor not found."); + ret = LY_EINVAL; + break; #endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGERR(root ? LYD_CTX(root) : NULL, LY_EINVAL, "Unknown data output format."); diff --git a/src/tree_data.c b/src/tree_data.c index 61069d189..9fd9580e5 100644 --- a/src/tree_data.c +++ b/src/tree_data.c @@ -138,6 +138,11 @@ lyd_parse(const struct ly_ctx *ctx, struct lyd_node *parent, struct lyd_node **f case LYD_CBOR: r = lyd_parse_cbor(ctx, NULL, parent, first_p, in, parse_opts, val_opts, int_opts, &parsed, NULL, &lydctx); break; +#else + case LYD_CBOR: + LOGARG(ctx, format); + r = LY_EINVAL; + break; #endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGARG(ctx, format); @@ -488,6 +493,11 @@ lyd_parse_op(const struct ly_ctx *ctx, struct lyd_node *parent, struct ly_in *in case LYD_CBOR: rc = lyd_parse_cbor(ctx, NULL, parent, &first, in, parse_options, val_opts, int_opts, &parsed, NULL, &lydctx); break; +#else + case LYD_CBOR: + LOGARG(ctx, format); + rc = LY_EINVAL; + break; #endif /* ENABLE_CBOR_SUPPORT */ case LYD_UNKNOWN: LOGARG(ctx, format);