Skip to content

Commit 2a5e854

Browse files
committed
refactor: Restructure CBOR implementation to match libyang conventions
- Reorganize high-level parser code in parser_cbor.c - Add common high-level context variables to parser_internal.h - Create lcbor.c and lcbor.h as a consistent wrapper over libcbor - Maintain coding style consistency with existing libyang modules
1 parent 8a41192 commit 2a5e854

File tree

6 files changed

+142
-90
lines changed

6 files changed

+142
-90
lines changed

src/lcbor.c

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,71 @@
2323
#include "log.h"
2424
#include "ly_common.h"
2525

26+
const char *
27+
lycbor_token2str(enum cbor_type cbortype)
28+
{
29+
switch (cbortype) {
30+
case CBOR_TYPE_UINT:
31+
return "unsigned integer";
32+
case CBOR_TYPE_NEGINT:
33+
return "negative integer";
34+
case CBOR_TYPE_BYTESTRING:
35+
return "byte string";
36+
case CBOR_TYPE_STRING:
37+
return "string";
38+
case CBOR_TYPE_ARRAY:
39+
return "array";
40+
case CBOR_TYPE_MAP:
41+
return "map";
42+
case CBOR_TYPE_TAG:
43+
return "tag";
44+
case CBOR_TYPE_FLOAT_CTRL:
45+
return "decimals and special values (true, false, nil, ...)";
46+
}
47+
48+
return "";
49+
}
50+
51+
return "object";
52+
case LYJSON_OBJECT_NEXT:
53+
return "object next";
54+
case LYJSON_OBJECT_CLOSED:
55+
return "object closed";
56+
case LYJSON_ARRAY:
57+
return "array";
58+
case LYJSON_ARRAY_NEXT:
59+
return "array next";
60+
case LYJSON_ARRAY_CLOSED:
61+
return "array closed";
62+
case LYJSON_OBJECT_NAME:
63+
return "object name";
64+
case LYJSON_NUMBER:
65+
return "number";
66+
case LYJSON_STRING:
67+
return "string";
68+
case LYJSON_TRUE:
69+
return "true";
70+
case LYJSON_FALSE:
71+
return "false";
72+
case LYJSON_NULL:
73+
return "null";
74+
case LYJSON_END:
75+
return "end of input";
76+
}
77+
78+
return "";
79+
}
80+
2681
/**
2782
* @brief Free CBOR context.
2883
*
29-
* @param[in] cbor_ctx CBOR context to free.
84+
* @param[in] cborctx CBOR context to free.
3085
*/
31-
void lycbor_ctx_free(struct lycbor_ctx *cbor_ctx)
86+
void lycbor_ctx_free(struct lycbor_ctx *cborctx)
3287
{
33-
if (cbor_ctx)
88+
if (cborctx)
3489
{
35-
free(cbor_ctx);
90+
free(cborctx);
3691
}
3792
}
3893

@@ -58,31 +113,48 @@ lydcbor_detect_format(struct ly_in *in, enum lyd_cbor_format *format)
58113
*
59114
* @param[in] ctx libyang context.
60115
* @param[in] in Input handler.
61-
* @param[out] cbor_ctx_p Pointer to store the created CBOR context.
116+
* @param[out] cborctx_p Pointer to store the created CBOR context.
62117
* @return LY_ERR value.
63118
*/
64119
LY_ERR
65-
lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p)
120+
lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p)
66121
{
67122
/* TODO : Need to restructure error handling here */
68123
LY_ERR ret = LY_SUCCESS;
69-
struct lycbor_ctx *cbor_ctx;
124+
struct lycbor_ctx *cborctx;
125+
struct cbor_load_result result = {0};
70126
enum lyd_cbor_format format;
71127

72-
assert(ctx && in && cbor_ctx_p);
128+
assert(ctx && in && cborctx_p);
73129

74130
/* TODO : error handling after the detect_format function call */
75131
ret = lydcbor_detect_format(in, &format);
76132

77133
/* Allocate and initialize CBOR context */
78-
cbor_ctx = calloc(1, sizeof *cbor_ctx);
79-
LY_CHECK_ERR_RET(!cbor_ctx, LOGMEM(ctx), LY_EMEM);
134+
cborctx = calloc(1, sizeof *cborctx);
135+
LY_CHECK_ERR_RET(!cborctx, LOGMEM(ctx), LY_EMEM);
136+
cborctx->ctx = ctx;
137+
cborctx->in = in;
138+
cborctx->format = format;
139+
140+
/* load and parse CBOR data */
141+
cborctx->cbor_data = cbor_load(in->current, in->length, &result);
142+
if (!cborctx->cbor_data) {
143+
LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data.");
144+
free(cborctx);
145+
return LY_EVALID;
146+
}
147+
if (result.error.code != CBOR_ERR_NONE) {
148+
LOGVAL(ctx, LYVE_SYNTAX, "CBOR parsing error (code %d).", result.error.code);
149+
cbor_decref(&cborctx->cbor_data);
150+
free(cborctx);
151+
return LY_EVALID;
152+
}
80153

81-
cbor_ctx->ctx = ctx;
82-
cbor_ctx->in = in;
83-
cbor_ctx->format = format;
154+
/* input line logging */
155+
ly_log_location(NULL, NULL, NULL, in);
84156

85-
*cbor_ctx_p = cbor_ctx;
157+
*cborctx_p = cborctx;
86158
return ret;
87159
}
88160

src/lcbor.h

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,30 +41,36 @@ enum lyd_cbor_format
4141
struct lycbor_ctx {
4242
const struct ly_ctx *ctx; /**< libyang context */
4343
struct ly_in *in; /**< input structure */
44+
4445
cbor_item_t *cbor_data; /**< parsed CBOR data */
46+
4547
enum lyd_cbor_format format; /**< CBOR format variant */
46-
uint32_t parse_opts; /**< parser options */
47-
uint32_t val_opts; /**< validation options */
48+
49+
struct {
50+
cbor_item_t *cbor_data; /**< parsed CBOR data */
51+
enum lyd_cbor_format format; /**< CBOR format variant */
52+
const char *input;
53+
} backup;
4854
};
4955

5056
/**
5157
* @brief Create new CBOR context for parsing.
5258
*
5359
* @param[in] ctx libyang context.
5460
* @param[in] in Input handler.
55-
* @param[out] cbor_ctx_p Pointer to store the created CBOR context.
61+
* @param[out] cborctx_p Pointer to store the created CBOR context.
5662
* @return LY_ERR value.
5763
*/
5864
LY_ERR
59-
lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cbor_ctx_p);
65+
lycbor_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lycbor_ctx **cborctx_p);
6066

6167
/**
6268
* @brief Free CBOR context.
6369
*
64-
* @param[in] cbor_ctx CBOR context to free.
70+
* @param[in] cborctx CBOR context to free.
6571
*/
6672
void
67-
lycbor_ctx_free(struct lycbor_ctx *cbor_ctx);
73+
lycbor_ctx_free(struct lycbor_ctx *cborctx);
6874

6975
#endif /* ENABLE_CBOR_SUPPORT */
7076

src/log.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ typedef enum {
271271
LYVE_SEMANTICS, /**< generic semantic error */
272272
LYVE_SYNTAX_XML, /**< XML-related syntax error */
273273
LYVE_SYNTAX_JSON, /**< JSON-related syntax error */
274+
LYVE_SYNTAX_CBOR, /**< CBOR-related syntax error */
274275
LYVE_DATA, /**< YANG data does not reflect some of the module restrictions */
275276

276277
LYVE_OTHER /**< Unknown error */

src/parser_cbor.c

Lines changed: 40 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,15 +1058,6 @@ lydcbor_subtree_r(struct lyd_cbor_ctx *lydctx, struct lyd_node *parent,
10581058

10591059
assert(lydctx && first_p && parsed && cbor_obj);
10601060

1061-
/* assuming that the top level structure is always a map
1062-
to be modified to include anything else that it can support */
1063-
1064-
if (!cbor_isa_map(cbor_obj))
1065-
{
1066-
LOGVAL(lydctx->cborctx->ctx, LYVE_SYNTAX, "Expected CBOR map");
1067-
return LY_EVALID;
1068-
}
1069-
10701061
size_t map_size = cbor_map_size(cbor_obj);
10711062
struct cbor_pair *pairs = cbor_map_handle(cbor_obj);
10721063

@@ -1159,28 +1150,37 @@ lydcbor_ctx_init(const struct ly_ctx *ctx, struct ly_in *in, uint32_t parse_opts
11591150
uint32_t val_opts, struct lyd_cbor_ctx **lydctx_p)
11601151
{
11611152
LY_ERR ret = LY_SUCCESS;
1162-
struct lyd_cbor_ctx *lydctx = NULL;
1153+
struct lyd_cbor_ctx *lydctx;
1154+
enum cbor_type cbortype;
11631155

11641156
assert(lydctx_p);
11651157

1166-
/* Initialize context with calloc to ensure all fields are zero */
1158+
/* init context */
11671159
lydctx = calloc(1, sizeof *lydctx);
11681160
LY_CHECK_ERR_RET(!lydctx, LOGMEM(ctx), LY_EMEM);
11691161
lydctx->parse_opts = parse_opts;
11701162
lydctx->val_opts = val_opts;
11711163
lydctx->free = lyd_cbor_ctx_free;
11721164

1173-
/* Create low-level CBOR context */
1174-
LY_CHECK_GOTO(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), cleanup);
1175-
1176-
*lydctx_p = lydctx;
1177-
return ret;
1165+
/* Create low-level CBOR context (includes CBOR parsing) */
1166+
LY_CHECK_ERR_RET(ret = lycbor_ctx_new(ctx, in, &lydctx->cborctx), free(lydctx), ret);
1167+
cbortype = cbor_typeof(lydctx->cborctx->cbor_data);
11781168

1179-
cleanup:
1180-
if (lydctx)
1169+
/* assuming that the top level structure is always a map
1170+
- though this is not mentioned explicitly in RFC9254 - it is implied
1171+
and it is almost always the case - This is a similar assumption made
1172+
to the RFC 7951 where JSON Encoding of data modeled by YANG is always assumed
1173+
to a have a top-level structure as an object */
1174+
if (!cbor_isa_map(lydctx->cborctx->cbor_data))
11811175
{
1176+
/* expecting top-level map */
1177+
LOGVAL(ctx, LYVE_SYNTAX_CBOR, "Expected top-level CBOR map, but %s found.", lycbor_token2str(cbortype));
1178+
*lydctx_p = NULL;
11821179
lyd_cbor_ctx_free((struct lyd_ctx *)lydctx);
1180+
return LY_EVALID;
11831181
}
1182+
1183+
*lydctx_p = lydctx;
11841184
return ret;
11851185
}
11861186

@@ -1189,72 +1189,43 @@ lyd_parse_cbor(const struct ly_ctx *ctx, const struct lysc_ext_instance *ext, st
11891189
struct lyd_node **first_p, struct ly_in *in, uint32_t parse_opts, uint32_t val_opts, uint32_t int_opts,
11901190
struct ly_set *parsed, ly_bool *subtree_sibling, struct lyd_ctx **lydctx_p)
11911191
{
1192-
LY_ERR ret = LY_SUCCESS;
1192+
LY_ERR r, rc = LY_SUCCESS;
11931193
struct lyd_cbor_ctx *lydctx = NULL;
1194-
cbor_item_t *cbor_data = NULL;
1195-
struct cbor_load_result result = {0};
1194+
printf("Entering lyd_parse_cbor\n AHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH\n");
11961195

1197-
/* Initialize context */
1198-
LY_CHECK_GOTO(ret = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, &lydctx), cleanup);
1196+
/* Initialize context (CBOR parsing happens in lycbor_ctx_new) */
1197+
rc = lydcbor_ctx_init(ctx, in, parse_opts, val_opts, &lydctx);
1198+
LY_CHECK_GOTO(rc, cleanup);
11991199

12001200
lydctx->int_opts = int_opts;
12011201
lydctx->ext = ext;
12021202

12031203
/* find the operation node if it exists already */
1204-
LY_CHECK_GOTO(ret = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
1205-
1206-
1207-
/*
1208-
* Loads CBOR data from the current input buffer.
1209-
*
1210-
* Parameters:
1211-
* in->current - Pointer to the current position in the input buffer.
1212-
* in->length - Length of the data to be loaded.
1213-
* &result - Pointer to a variable where the result status will be stored.
1214-
*
1215-
* Returns:
1216-
* cbor_data - Pointer to the loaded CBOR data structure, or NULL on failure.
1217-
*/
1218-
/* need to convert in->current from const char* to cbor_data type */
1219-
cbor_data = cbor_load(in->current, in->length, &result);
1220-
lydctx->cborctx->cbor_data = cbor_data;
1221-
1222-
if (!cbor_data)
1223-
{
1224-
LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: no data returned from cbor_load().");
1225-
ret = LY_EVALID;
1226-
goto cleanup;
1227-
}
1228-
if (result.error.code != CBOR_ERR_NONE)
1229-
{
1230-
LOGVAL(ctx, LYVE_SYNTAX, "Failed to parse CBOR data: parsing error (code %d).", result.error.code);
1231-
ret = LY_EVALID;
1232-
goto cleanup;
1233-
}
1204+
LY_CHECK_GOTO(rc = lyd_parser_find_operation(parent, int_opts, &lydctx->op_node), cleanup);
12341205

1235-
/* Probably need to check if the obtained data is a operational node and
1236-
then write functions to parse them accordingly. If not then continue below */
1206+
/* Parse the CBOR structure - read subtrees */
1207+
r = lydcbor_subtree_r(lydctx, parent, first_p, parsed, lydctx->cborctx->cbor_data);
1208+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
12371209

1238-
/* Parse the CBOR structure */
1239-
ret = lydcbor_subtree_r(lydctx, parent, first_p, parsed, cbor_data);
1210+
/* Unexpected sibling node error handling */
12401211

1241-
cleanup:
1242-
if (cbor_data)
1243-
{
1244-
cbor_decref(&cbor_data);
1212+
/* Validate operation node presence */
1213+
if ((int_opts & (LYD_INTOPT_RPC | LYD_INTOPT_ACTION | LYD_INTOPT_NOTIF | LYD_INTOPT_REPLY)) &&
1214+
!lydctx->op_node) {
1215+
LOGVAL(ctx, LYVE_DATA, "Missing the operation node.");
1216+
r = LY_EVALID;
1217+
LY_DPARSER_ERR_GOTO(r, rc = r, lydctx, cleanup);
12451218
}
1219+
/* also need to deal with metadata linking etc*/
12461220

1247-
if (ret)
1248-
{
1249-
if (lydctx)
1250-
{
1251-
lyd_cbor_ctx_free((struct lyd_ctx *)lydctx);
1252-
lydctx = NULL;
1253-
}
1221+
cleanup:
1222+
if (rc && (!lydctx || !(lydctx->val_opts & LYD_VALIDATE_MULTI_ERROR) || (rc != LY_EVALID))) {
1223+
lyd_cbor_ctx_free((struct lyd_ctx *)lydctx);
1224+
lydctx = NULL;
12541225
}
12551226

12561227
*lydctx_p = (struct lyd_ctx *)lydctx;
1257-
return ret;
1228+
return rc;
12581229
}
12591230

12601231
#endif /* ENABLE_CBOR_SUPPORT */

src/parser_data.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ struct ly_in;
4444
* - CBOR
4545
*
4646
* The reference documentation would be `Encoding of Data Modeled with YANG in the Concise Binary Object
47-
* Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254)
47+
* Representation (CBOR)` : [RFC 9254](https://datatracker.ietf.org/doc/html/rfc9254). < $TODO$ Look at the edge cases of
48+
* RPCs, actions and Notifications and maybe like json only - where we make a proprietary representation>
4849
*
4950
* While the parsers themselves process the input data only syntactically, all the parser functions actually incorporate
5051
* the [common validator](@ref howtoDataValidation) checking the input data semantically. Therefore, the parser functions

src/parser_internal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ struct lyd_cbor_ctx
204204
lyd_ctx_free_clb free; /**< destructor */
205205

206206
struct lycbor_ctx *cborctx; /**< CBOR context for low-level operations */
207+
const struct lysc_node *any_schema; /**< parent anyxml/anydata schema node if parsing nested data tree */
207208
};
208209
#endif /* ENABLE_CBOR_SUPPORT */
209210

0 commit comments

Comments
 (0)