diff options
Diffstat (limited to '')
-rw-r--r-- | src/json.c | 669 |
1 files changed, 357 insertions, 312 deletions
@@ -1,9 +1,10 @@ /** * @file json.c * @author Radek Krejci <rkrejci@cesnet.cz> + * @author Michal Vasko <mvasko@cesnet.cz> * @brief Generic JSON format parser for libyang * - * Copyright (c) 2020 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. @@ -30,30 +31,30 @@ lyjson_token2str(enum LYJSON_PARSER_STATUS status) switch (status) { case LYJSON_ERROR: return "error"; - case LYJSON_ROOT: - return "document root"; - case LYJSON_FALSE: - return "false"; - case LYJSON_TRUE: - return "true"; - case LYJSON_NULL: - return "null"; case LYJSON_OBJECT: return "object"; + case LYJSON_OBJECT_NEXT: + return "object next"; case LYJSON_OBJECT_CLOSED: return "object closed"; - case LYJSON_OBJECT_EMPTY: - return "empty object"; case LYJSON_ARRAY: return "array"; + case LYJSON_ARRAY_NEXT: + return "array next"; case LYJSON_ARRAY_CLOSED: return "array closed"; - case LYJSON_ARRAY_EMPTY: - return "empty array"; + 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"; } @@ -61,25 +62,48 @@ lyjson_token2str(enum LYJSON_PARSER_STATUS status) return ""; } -static LY_ERR -skip_ws(struct lyjson_ctx *jsonctx) +enum LYJSON_PARSER_STATUS +lyjson_ctx_status(struct lyjson_ctx *jsonctx) { - /* skip leading whitespaces */ - while (*jsonctx->in->current != '\0' && is_jsonws(*jsonctx->in->current)) { + assert(jsonctx); + + if (!jsonctx->status.count) { + return LYJSON_END; + } + + return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - 1]; +} + +uint32_t +lyjson_ctx_depth(struct lyjson_ctx *jsonctx) +{ + return jsonctx->status.count; +} + +/** + * @brief Skip WS in the JSON context. + * + * @param[in] jsonctx JSON parser context. + */ +static void +lyjson_skip_ws(struct lyjson_ctx *jsonctx) +{ + /* skip whitespaces */ + while (is_jsonws(*jsonctx->in->current)) { if (*jsonctx->in->current == '\n') { LY_IN_NEW_LINE(jsonctx->in); } ly_in_skip(jsonctx->in, 1); } - if (*jsonctx->in->current == '\0') { - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_END); - } - - return LY_SUCCESS; } -/* - * @brief Set value corresponding to the current context's status +/** + * @brief Set value in the JSON context. + * + * @param[in] jsonctx JSON parser context. + * @param[in] value Value to set. + * @param[in] value_len Length of @p value. + * @param[in] dynamic Whether @p value is dynamically-allocated. */ static void lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value_len, ly_bool dynamic) @@ -94,48 +118,24 @@ lyjson_ctx_set_value(struct lyjson_ctx *jsonctx, const char *value, size_t value jsonctx->dynamic = dynamic; } -static LY_ERR -lyjson_check_next(struct lyjson_ctx *jsonctx) -{ - if (jsonctx->status.count == 1) { - /* top level value (JSON-text), ws expected */ - if ((*jsonctx->in->current == '\0') || is_jsonws(*jsonctx->in->current)) { - return LY_SUCCESS; - } - } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_OBJECT) { - LY_CHECK_RET(skip_ws(jsonctx)); - if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == '}')) { - return LY_SUCCESS; - } - } else if (lyjson_ctx_status(jsonctx, 1) == LYJSON_ARRAY) { - LY_CHECK_RET(skip_ws(jsonctx)); - if ((*jsonctx->in->current == ',') || (*jsonctx->in->current == ']')) { - return LY_SUCCESS; - } - } - - LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Unexpected character \"%c\" after JSON %s.", - *jsonctx->in->current, lyjson_token2str(lyjson_ctx_status(jsonctx, 0))); - return LY_EVALID; -} - /** - * Input is expected to start after the opening quotation-mark. - * When succeeds, input is moved after the closing quotation-mark. + * @brief Parse a JSON string (starting after double quotes) and store it in the context. + * + * @param[in] jsonctx JSON parser context. + * @return LY_ERR value. */ static LY_ERR -lyjson_string_(struct lyjson_ctx *jsonctx) +lyjson_string(struct lyjson_ctx *jsonctx) { -#define BUFSIZE 24 -#define BUFSIZE_STEP 128 - - const char *in = jsonctx->in->current, *start; + const char *in = jsonctx->in->current, *start, *c; char *buf = NULL; size_t offset; /* read offset in input buffer */ size_t len; /* length of the output string (write offset in output buffer) */ size_t size = 0; /* size of the output buffer */ size_t u; uint64_t start_line; + uint32_t value; + uint8_t i; assert(jsonctx); @@ -146,17 +146,15 @@ lyjson_string_(struct lyjson_ctx *jsonctx) /* parse */ while (in[offset]) { - if (in[offset] == '\\') { + switch (in[offset]) { + case '\\': /* escape sequence */ - const char *slash = &in[offset]; - uint32_t value; - uint8_t i = 1; - + c = &in[offset]; if (!buf) { /* prepare output buffer */ - buf = malloc(BUFSIZE); + buf = malloc(LYJSON_STRING_BUF_START); LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM); - size = BUFSIZE; + size = LYJSON_STRING_BUF_START; } /* allocate enough for the offset and next character, @@ -165,10 +163,10 @@ lyjson_string_(struct lyjson_ctx *jsonctx) if (len + offset + 4 >= size) { size_t increment; - for (increment = BUFSIZE_STEP; len + offset + 4 >= size + increment; increment += BUFSIZE_STEP) {} + for (increment = LYJSON_STRING_BUF_STEP; len + offset + 4 >= size + increment; increment += LYJSON_STRING_BUF_STEP) {} buf = ly_realloc(buf, size + increment); LY_CHECK_ERR_RET(!buf, LOGMEM(jsonctx->ctx), LY_EMEM); - size += BUFSIZE_STEP; + size += LYJSON_STRING_BUF_STEP; } if (offset) { @@ -179,6 +177,7 @@ lyjson_string_(struct lyjson_ctx *jsonctx) offset = 0; } + i = 1; switch (in[++offset]) { case '"': /* quotation mark */ @@ -217,7 +216,7 @@ lyjson_string_(struct lyjson_ctx *jsonctx) offset++; for (value = i = 0; i < 4; i++) { if (!in[offset + i]) { - LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", slash); + LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid basic multilingual plane character \"%s\".", c); goto error; } else if (isdigit(in[offset + i])) { u = (in[offset + i] - '0'); @@ -239,13 +238,14 @@ lyjson_string_(struct lyjson_ctx *jsonctx) offset += i; /* add read escaped characters */ LY_CHECK_ERR_GOTO(ly_pututf8(&buf[len], value, &u), LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character reference \"%.*s\" (0x%08x).", - (int)(&in[offset] - slash), slash, value), + (int)(&in[offset] - c), c, value), error); len += u; /* update number of bytes in buffer */ in += offset; /* move the input by the processed bytes stored in the buffer ... */ offset = 0; /* ... and reset the offset index for future moving data into buffer */ + break; - } else if (in[offset] == '"') { + case '"': /* end of string */ if (buf) { /* realloc exact size string */ @@ -263,22 +263,21 @@ lyjson_string_(struct lyjson_ctx *jsonctx) ++offset; in += offset; goto success; - } else { - /* get it as UTF-8 character for check */ - const char *c = &in[offset]; - uint32_t code = 0; - size_t code_len = 0; - LY_CHECK_ERR_GOTO(ly_getutf8(&c, &code, &code_len), + default: + /* get it as UTF-8 character for check */ + c = &in[offset]; + LY_CHECK_ERR_GOTO(ly_getutf8(&c, &value, &u), LOGVAL(jsonctx->ctx, LY_VCODE_INCHAR, in[offset]), error); - LY_CHECK_ERR_GOTO(!is_jsonstrchar(code), + LY_CHECK_ERR_GOTO(!is_jsonstrchar(value), LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Invalid character in JSON string \"%.*s\" (0x%08x).", - (int)(&in[offset] - start + code_len), start, code), + (int)(&in[offset] - start + u), start, value), error); /* character is ok, continue */ - offset += code_len; + offset += u; + break; } } @@ -299,24 +298,6 @@ success: } return LY_SUCCESS; - -#undef BUFSIZE -#undef BUFSIZE_STEP -} - -/* - * - * Wrapper around lyjson_string_() adding LYJSON_STRING status into context to allow using lyjson_string_() for parsing object's name. - */ -static LY_ERR -lyjson_string(struct lyjson_ctx *jsonctx) -{ - LY_CHECK_RET(lyjson_string_(jsonctx)); - - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING); - LY_CHECK_RET(lyjson_check_next(jsonctx)); - - return LY_SUCCESS; } /** @@ -414,8 +395,7 @@ lyjson_get_buffer_for_number(const struct ly_ctx *ctx, uint64_t num_len, char ** * @return Number of characters written to the @p dst. */ static uint32_t -lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, - char *dec_point, int32_t dp_position, char *dst) +lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, char *dec_point, int32_t dp_position, char *dst) { int32_t dec_point_idx; int32_t n, d; @@ -456,8 +436,8 @@ lyjson_exp_number_copy_num_part(const char *num, uint32_t num_len, * @return LY_ERR value. */ static LY_ERR -lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, - uint64_t total_len, char **res, size_t *res_len) +lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent, uint64_t total_len, char **res, + size_t *res_len) { #define MAYBE_WRITE_MINUS(ARRAY, INDEX, FLAG) \ @@ -642,6 +622,12 @@ lyjson_exp_number(const struct ly_ctx *ctx, const char *in, const char *exponent return LY_SUCCESS; } +/** + * @brief Parse a JSON number and store it in the context. + * + * @param[in] jsonctx JSON parser context. + * @return LY_ERR value. + */ static LY_ERR lyjson_number(struct lyjson_ctx *jsonctx) { @@ -713,140 +699,270 @@ invalid_character: } ly_in_skip(jsonctx->in, offset); - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER); - LY_CHECK_RET(lyjson_check_next(jsonctx)); - return LY_SUCCESS; } -static LY_ERR -lyjson_object_name(struct lyjson_ctx *jsonctx) +LY_ERR +lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p) { - if (*jsonctx->in->current != '"') { - LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), - jsonctx->in->current, "a JSON object's member"); - return LY_EVALID; - } - ly_in_skip(jsonctx->in, 1); + LY_ERR ret = LY_SUCCESS; + struct lyjson_ctx *jsonctx; - LY_CHECK_RET(lyjson_string_(jsonctx)); - LY_CHECK_RET(skip_ws(jsonctx)); - if (*jsonctx->in->current != ':') { - LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current, - "a JSON object's name-separator ':'"); - return LY_EVALID; + assert(ctx && in && jsonctx_p); + + /* new context */ + jsonctx = calloc(1, sizeof *jsonctx); + LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM); + jsonctx->ctx = ctx; + jsonctx->in = in; + + LOG_LOCSET(NULL, NULL, NULL, in); + + /* WS are always expected to be skipped */ + lyjson_skip_ws(jsonctx); + + if (jsonctx->in->current[0] == '\0') { + /* empty file, invalid */ + LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Empty JSON file."); + ret = LY_EVALID; + goto cleanup; } - ly_in_skip(jsonctx->in, 1); - LY_CHECK_RET(skip_ws(jsonctx)); - return LY_SUCCESS; + /* start JSON parsing */ + LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup); + +cleanup: + if (ret) { + lyjson_ctx_free(jsonctx); + } else { + *jsonctx_p = jsonctx; + } + return ret; } +/** + * @brief Parse next JSON token, object-name is expected. + * + * @param[in] jsonctx JSON parser context. + * @return LY_ERR value. + */ static LY_ERR -lyjson_object(struct lyjson_ctx *jsonctx) +lyjson_next_object_name(struct lyjson_ctx *jsonctx) { - LY_CHECK_RET(skip_ws(jsonctx)); + switch (*jsonctx->in->current) { + case '\0': + /* EOF */ + LOGVAL(jsonctx->ctx, LY_VCODE_EOF); + return LY_EVALID; - if (*jsonctx->in->current == '}') { - assert(jsonctx->depth); - jsonctx->depth--; - /* empty object */ + case '"': + /* object name */ ly_in_skip(jsonctx->in, 1); - lyjson_ctx_set_value(jsonctx, NULL, 0, 0); - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_EMPTY); - return LY_SUCCESS; - } + LY_CHECK_RET(lyjson_string(jsonctx)); + lyjson_skip_ws(jsonctx); - LY_CHECK_RET(lyjson_object_name(jsonctx)); + if (*jsonctx->in->current != ':') { + LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current, + "a JSON value name-separator ':'"); + return LY_EVALID; + } + ly_in_skip(jsonctx->in, 1); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME); + break; - /* output data are set by lyjson_string_() */ - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT); + case '}': + /* object end */ + ly_in_skip(jsonctx->in, 1); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED); + break; + + default: + /* unexpected value */ + LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), + jsonctx->in->current, "a JSON object name"); + return LY_EVALID; + } return LY_SUCCESS; } /** - * @brief Process JSON array envelope + * @brief Parse next JSON token, value is expected. * - * @param[in] jsonctx JSON parser context - * @return LY_SUCCESS or LY_EMEM + * @param[in] jsonctx JSON parser context. + * @param[in] array_end Whether array-end is accepted or not. + * @return LY_ERR value. */ static LY_ERR -lyjson_array(struct lyjson_ctx *jsonctx) +lyjson_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end) { - LY_CHECK_RET(skip_ws(jsonctx)); + switch (*jsonctx->in->current) { + case '\0': + /* EOF */ + LOGVAL(jsonctx->ctx, LY_VCODE_EOF); + return LY_EVALID; - if (*jsonctx->in->current == ']') { - /* empty array */ + case '"': + /* string */ + ly_in_skip(jsonctx->in, 1); + LY_CHECK_RET(lyjson_string(jsonctx)); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_STRING); + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* number */ + LY_CHECK_RET(lyjson_number(jsonctx)); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NUMBER); + break; + + case '{': + /* object */ + ly_in_skip(jsonctx->in, 1); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT); + break; + + case '[': + /* array */ ly_in_skip(jsonctx->in, 1); - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_EMPTY); - } else { LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY); - } + break; - /* erase previous values, array has no value on its own */ - lyjson_ctx_set_value(jsonctx, NULL, 0, 0); + case 't': + if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) { + goto unexpected_value; + } - return LY_SUCCESS; -} + /* true */ + lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0); + ly_in_skip(jsonctx->in, ly_strlen_const("true")); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE); + break; -static LY_ERR -lyjson_value(struct lyjson_ctx *jsonctx) -{ - if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) { - return LY_SUCCESS; - } + case 'f': + if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) { + goto unexpected_value; + } - if ((*jsonctx->in->current == 'f') && !strncmp(jsonctx->in->current, "false", ly_strlen_const("false"))) { /* false */ lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("false"), 0); ly_in_skip(jsonctx->in, ly_strlen_const("false")); LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_FALSE); - LY_CHECK_RET(lyjson_check_next(jsonctx)); + break; - } else if ((*jsonctx->in->current == 't') && !strncmp(jsonctx->in->current, "true", ly_strlen_const("true"))) { - /* true */ - lyjson_ctx_set_value(jsonctx, jsonctx->in->current, ly_strlen_const("true"), 0); - ly_in_skip(jsonctx->in, ly_strlen_const("true")); - LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_TRUE); - LY_CHECK_RET(lyjson_check_next(jsonctx)); + case 'n': + if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) { + goto unexpected_value; + } - } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) { - /* none */ + /* null */ lyjson_ctx_set_value(jsonctx, "", 0, 0); ly_in_skip(jsonctx->in, ly_strlen_const("null")); LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL); - LY_CHECK_RET(lyjson_check_next(jsonctx)); + break; - } else if (*jsonctx->in->current == '"') { - /* string */ + case ']': + if (!array_end) { + goto unexpected_value; + } + + /* array end */ ly_in_skip(jsonctx->in, 1); - LY_CHECK_RET(lyjson_string(jsonctx)); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED); + break; - } else if (*jsonctx->in->current == '[') { - /* array */ + default: +unexpected_value: + LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), + jsonctx->in->current, "a JSON value"); + return LY_EVALID; + } + + if (jsonctx->status.count > LY_MAX_BLOCK_DEPTH * 10) { + LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of nestings has been exceeded.", LY_MAX_BLOCK_DEPTH * 10); + return LY_EINVAL; + } + + return LY_SUCCESS; +} + +/** + * @brief Parse next JSON token, object-next-item is expected. + * + * @param[in] jsonctx JSON parser context. + * @return LY_ERR value. + */ +static LY_ERR +lyjson_next_object_item(struct lyjson_ctx *jsonctx) +{ + switch (*jsonctx->in->current) { + case '\0': + /* EOF */ + LOGVAL(jsonctx->ctx, LY_VCODE_EOF); + return LY_EVALID; + + case '}': + /* object end */ ly_in_skip(jsonctx->in, 1); - LY_CHECK_RET(lyjson_array(jsonctx)); - - } else if (*jsonctx->in->current == '{') { - jsonctx->depth++; - if (jsonctx->depth > LY_MAX_BLOCK_DEPTH) { - LOGERR(jsonctx->ctx, LY_EINVAL, - "The maximum number of block nestings has been exceeded."); - return LY_EINVAL; - } - /* object */ + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED); + break; + + case ',': + /* next object item */ ly_in_skip(jsonctx->in, 1); - LY_CHECK_RET(lyjson_object(jsonctx)); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT); + break; - } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) { - /* number */ - LY_CHECK_RET(lyjson_number(jsonctx)); + default: + /* unexpected value */ + LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), + jsonctx->in->current, "a JSON object-end or next item"); + return LY_EVALID; + } - } else { + return LY_SUCCESS; +} + +/** + * @brief Parse next JSON token, array-next-item is expected. + * + * @param[in] jsonctx JSON parser context. + * @return LY_ERR value. + */ +static LY_ERR +lyjson_next_array_item(struct lyjson_ctx *jsonctx) +{ + switch (*jsonctx->in->current) { + case '\0': + /* EOF */ + LOGVAL(jsonctx->ctx, LY_VCODE_EOF); + return LY_EVALID; + + case ']': + /* array end */ + ly_in_skip(jsonctx->in, 1); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED); + break; + + case ',': + /* next array item */ + ly_in_skip(jsonctx->in, 1); + LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_NEXT); + break; + + default: /* unexpected value */ LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), - jsonctx->in->current, "a JSON value"); + jsonctx->in->current, "a JSON array-end or next item"); return LY_EVALID; } @@ -854,46 +970,72 @@ lyjson_value(struct lyjson_ctx *jsonctx) } LY_ERR -lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx_p) +lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status) { LY_ERR ret = LY_SUCCESS; - struct lyjson_ctx *jsonctx; + enum LYJSON_PARSER_STATUS cur; - assert(ctx); - assert(in); - assert(jsonctx_p); - - /* new context */ - jsonctx = calloc(1, sizeof *jsonctx); - LY_CHECK_ERR_RET(!jsonctx, LOGMEM(ctx), LY_EMEM); - jsonctx->ctx = ctx; - jsonctx->in = in; + assert(jsonctx); - LOG_LOCSET(NULL, NULL, NULL, in); + cur = lyjson_ctx_status(jsonctx); + switch (cur) { + case LYJSON_OBJECT: + LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup); + break; + case LYJSON_ARRAY: + LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 1), cleanup); + break; + case LYJSON_OBJECT_NEXT: + LYJSON_STATUS_POP(jsonctx); + LY_CHECK_GOTO(ret = lyjson_next_object_name(jsonctx), cleanup); + break; + case LYJSON_ARRAY_NEXT: + LYJSON_STATUS_POP(jsonctx); + LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup); + break; + case LYJSON_OBJECT_NAME: + lyjson_ctx_set_value(jsonctx, NULL, 0, 0); + LYJSON_STATUS_POP(jsonctx); + LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup); + break; + case LYJSON_OBJECT_CLOSED: + case LYJSON_ARRAY_CLOSED: + LYJSON_STATUS_POP(jsonctx); + /* fallthrough */ + case LYJSON_NUMBER: + case LYJSON_STRING: + case LYJSON_TRUE: + case LYJSON_FALSE: + case LYJSON_NULL: + lyjson_ctx_set_value(jsonctx, NULL, 0, 0); + LYJSON_STATUS_POP(jsonctx); + cur = lyjson_ctx_status(jsonctx); + + if (cur == LYJSON_OBJECT) { + LY_CHECK_GOTO(ret = lyjson_next_object_item(jsonctx), cleanup); + break; + } else if (cur == LYJSON_ARRAY) { + LY_CHECK_GOTO(ret = lyjson_next_array_item(jsonctx), cleanup); + break; + } - /* parse JSON value, if any */ - LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup); - if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) { - /* empty data input */ + assert(cur == LYJSON_END); + goto cleanup; + case LYJSON_END: + LY_CHECK_GOTO(ret = lyjson_next_value(jsonctx, 0), cleanup); + break; + case LYJSON_ERROR: + LOGINT(jsonctx->ctx); + ret = LY_EINT; goto cleanup; } - if (subtree) { - ret = lyjson_object(jsonctx); - jsonctx->depth++; - } else { - ret = lyjson_value(jsonctx); - } - if ((jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) { - LOGVAL(jsonctx->ctx, LY_VCODE_EOF); - ret = LY_EVALID; - } + /* skip WS */ + lyjson_skip_ws(jsonctx); cleanup: - if (ret) { - lyjson_ctx_free(jsonctx); - } else { - *jsonctx_p = jsonctx; + if (!ret && status) { + *status = lyjson_ctx_status(jsonctx); } return ret; } @@ -904,13 +1046,12 @@ lyjson_ctx_backup(struct lyjson_ctx *jsonctx) if (jsonctx->backup.dynamic) { free((char *)jsonctx->backup.value); } - jsonctx->backup.status = lyjson_ctx_status(jsonctx, 0); + jsonctx->backup.status = lyjson_ctx_status(jsonctx); jsonctx->backup.status_count = jsonctx->status.count; jsonctx->backup.value = jsonctx->value; jsonctx->backup.value_len = jsonctx->value_len; jsonctx->backup.input = jsonctx->in->current; jsonctx->backup.dynamic = jsonctx->dynamic; - jsonctx->backup.depth = jsonctx->depth; jsonctx->dynamic = 0; } @@ -926,105 +1067,9 @@ lyjson_ctx_restore(struct lyjson_ctx *jsonctx) jsonctx->value_len = jsonctx->backup.value_len; jsonctx->in->current = jsonctx->backup.input; jsonctx->dynamic = jsonctx->backup.dynamic; - jsonctx->depth = jsonctx->backup.depth; jsonctx->backup.dynamic = 0; } -LY_ERR -lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status) -{ - LY_ERR ret = LY_SUCCESS; - ly_bool toplevel = 0; - enum LYJSON_PARSER_STATUS prev; - - assert(jsonctx); - - prev = lyjson_ctx_status(jsonctx, 0); - - if ((prev == LYJSON_OBJECT) || (prev == LYJSON_ARRAY)) { - /* get value for the object's member OR the first value in the array */ - ret = lyjson_value(jsonctx); - goto result; - } else { - /* the previous token is closed and should be completely processed */ - LYJSON_STATUS_POP_RET(jsonctx); - prev = lyjson_ctx_status(jsonctx, 0); - } - - if (!jsonctx->status.count) { - /* we are done with the top level value */ - toplevel = 1; - } - LY_CHECK_RET(skip_ws(jsonctx)); - if (toplevel && !jsonctx->status.count) { - /* EOF expected, but there are some data after the top level token */ - LOGVAL(jsonctx->ctx, LYVE_SYNTAX, "Expecting end-of-input, but some data follows the top level JSON value."); - return LY_EVALID; - } - - if (toplevel) { - /* we are done */ - goto result; - } - - /* continue with the next token */ - assert(prev == LYJSON_OBJECT || prev == LYJSON_ARRAY); - - if (*jsonctx->in->current == ',') { - /* sibling item in the ... */ - ly_in_skip(jsonctx->in, 1); - LY_CHECK_RET(skip_ws(jsonctx)); - - if (prev == LYJSON_OBJECT) { - /* ... object - get another object's member */ - ret = lyjson_object_name(jsonctx); - } else { /* LYJSON_ARRAY */ - /* ... array - get another complete value */ - ret = lyjson_value(jsonctx); - } - } else if (((prev == LYJSON_OBJECT) && (*jsonctx->in->current == '}')) || - ((prev == LYJSON_ARRAY) && (*jsonctx->in->current == ']'))) { - if (*jsonctx->in->current == '}') { - assert(jsonctx->depth); - jsonctx->depth--; - } - ly_in_skip(jsonctx->in, 1); - LYJSON_STATUS_POP_RET(jsonctx); - LYJSON_STATUS_PUSH_RET(jsonctx, prev + 1); - } else { - /* unexpected value */ - LOGVAL(jsonctx->ctx, LY_VCODE_INSTREXP, LY_VCODE_INSTREXP_len(jsonctx->in->current), jsonctx->in->current, - prev == LYJSON_ARRAY ? "another JSON value in array" : "another JSON object's member"); - return LY_EVALID; - } - -result: - if ((ret == LY_SUCCESS) && (jsonctx->status.count > 1) && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) { - LOGVAL(jsonctx->ctx, LY_VCODE_EOF); - ret = LY_EVALID; - } - - if ((ret == LY_SUCCESS) && status) { - *status = lyjson_ctx_status(jsonctx, 0); - } - - return ret; -} - -enum LYJSON_PARSER_STATUS -lyjson_ctx_status(struct lyjson_ctx *jsonctx, uint32_t index) -{ - assert(jsonctx); - - if (jsonctx->status.count < index) { - return LYJSON_ERROR; - } else if (jsonctx->status.count == index) { - return LYJSON_ROOT; - } else { - return (enum LYJSON_PARSER_STATUS)(uintptr_t)jsonctx->status.objs[jsonctx->status.count - (index + 1)]; - } -} - void lyjson_ctx_free(struct lyjson_ctx *jsonctx) { |