summaryrefslogtreecommitdiffstats
path: root/src/json.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/json.c669
1 files changed, 357 insertions, 312 deletions
diff --git a/src/json.c b/src/json.c
index 5c45c8c..53f41dc 100644
--- a/src/json.c
+++ b/src/json.c
@@ -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)
{