json REFACTOR major rewrite of JSON parser
diff --git a/src/json.c b/src/json.c
index eeb7478..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 @@
     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 @@
     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 @@
     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 @@
 
     /* 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 @@
             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 @@
                 offset = 0;
             }
 
+            i = 1;
             switch (in[++offset]) {
             case '"':
                 /* quotation mark */
@@ -217,7 +216,7 @@
                 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 @@
             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 @@
             ++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 @@
     }
 
     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 @@
  * @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 @@
  * @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 @@
     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,159 +699,16 @@
     }
     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)
-{
-    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_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;
-    }
-    ly_in_skip(jsonctx->in, 1);
-    LY_CHECK_RET(skip_ws(jsonctx));
-
-    return LY_SUCCESS;
-}
-
-static LY_ERR
-lyjson_object(struct lyjson_ctx *jsonctx)
-{
-    LY_CHECK_RET(skip_ws(jsonctx));
-
-    if (*jsonctx->in->current == '}') {
-        assert(jsonctx->depth);
-        jsonctx->depth--;
-        /* empty object */
-        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_object_name(jsonctx));
-
-    /* output data are set by lyjson_string_() */
-    LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT);
-
-    return LY_SUCCESS;
-}
-
-/**
- * @brief Process JSON array envelope
- *
- * @param[in] jsonctx JSON parser context
- * @return LY_SUCCESS or LY_EMEM
- */
-static LY_ERR
-lyjson_array(struct lyjson_ctx *jsonctx)
-{
-    LY_CHECK_RET(skip_ws(jsonctx));
-
-    if (*jsonctx->in->current == ']') {
-        /* empty array */
-        ly_in_skip(jsonctx->in, 1);
-        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_EMPTY);
-    } else {
-        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY);
-    }
-
-    /* erase previous values, array has no value on its own */
-    lyjson_ctx_set_value(jsonctx, NULL, 0, 0);
-
-    return LY_SUCCESS;
-}
-
-static LY_ERR
-lyjson_value(struct lyjson_ctx *jsonctx)
-{
-    if (jsonctx->status.count && (lyjson_ctx_status(jsonctx, 0) == LYJSON_END)) {
-        return LY_SUCCESS;
-    }
-
-    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));
-
-    } 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));
-
-    } else if ((*jsonctx->in->current == 'n') && !strncmp(jsonctx->in->current, "null", ly_strlen_const("null"))) {
-        /* none */
-        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));
-
-    } else if (*jsonctx->in->current == '"') {
-        /* string */
-        ly_in_skip(jsonctx->in, 1);
-        LY_CHECK_RET(lyjson_string(jsonctx));
-
-    } else if (*jsonctx->in->current == '[') {
-        /* array */
-        ly_in_skip(jsonctx->in, 1);
-        LY_CHECK_RET(lyjson_array(jsonctx));
-
-    } else if (*jsonctx->in->current == '{') {
-        jsonctx->depth++;
-
-        /* object */
-        ly_in_skip(jsonctx->in, 1);
-        LY_CHECK_RET(lyjson_object(jsonctx));
-
-    } else if ((*jsonctx->in->current == '-') || ((*jsonctx->in->current >= '0') && (*jsonctx->in->current <= '9'))) {
-        /* number */
-        LY_CHECK_RET(lyjson_number(jsonctx));
-
-    } else {
-        /* 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->depth > LY_MAX_BLOCK_DEPTH) {
-        LOGERR(jsonctx->ctx, LY_EINVAL, "Maximum number %d of block nestings has been exceeded.", LY_MAX_BLOCK_DEPTH);
-        return LY_EINVAL;
-    } else 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;
 }
 
 LY_ERR
-lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, ly_bool subtree, struct lyjson_ctx **jsonctx_p)
+lyjson_ctx_new(const struct ly_ctx *ctx, struct ly_in *in, struct lyjson_ctx **jsonctx_p)
 {
     LY_ERR ret = LY_SUCCESS;
     struct lyjson_ctx *jsonctx;
 
-    assert(ctx);
-    assert(in);
-    assert(jsonctx_p);
+    assert(ctx && in && jsonctx_p);
 
     /* new context */
     jsonctx = calloc(1, sizeof *jsonctx);
@@ -875,23 +718,18 @@
 
     LOG_LOCSET(NULL, NULL, NULL, in);
 
-    /* parse JSON value, if any */
-    LY_CHECK_GOTO(ret = skip_ws(jsonctx), cleanup);
-    if (lyjson_ctx_status(jsonctx, 0) == LYJSON_END) {
-        /* empty data input */
+    /* 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;
     }
 
-    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;
-    }
+    /* start JSON parsing */
+    LY_CHECK_GOTO(ret = lyjson_ctx_next(jsonctx, NULL), cleanup);
 
 cleanup:
     if (ret) {
@@ -902,19 +740,318 @@
     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_next_object_name(struct lyjson_ctx *jsonctx)
+{
+    switch (*jsonctx->in->current) {
+    case '\0':
+        /* EOF */
+        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+        return LY_EVALID;
+
+    case '"':
+        /* object name */
+        ly_in_skip(jsonctx->in, 1);
+        LY_CHECK_RET(lyjson_string(jsonctx));
+        lyjson_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 value name-separator ':'");
+            return LY_EVALID;
+        }
+        ly_in_skip(jsonctx->in, 1);
+        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NAME);
+        break;
+
+    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 Parse next JSON token, value is expected.
+ *
+ * @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_next_value(struct lyjson_ctx *jsonctx, ly_bool array_end)
+{
+    switch (*jsonctx->in->current) {
+    case '\0':
+        /* EOF */
+        LOGVAL(jsonctx->ctx, LY_VCODE_EOF);
+        return LY_EVALID;
+
+    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);
+        break;
+
+    case 't':
+        if (strncmp(jsonctx->in->current + 1, "rue", ly_strlen_const("rue"))) {
+            goto unexpected_value;
+        }
+
+        /* 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;
+
+    case 'f':
+        if (strncmp(jsonctx->in->current + 1, "alse", ly_strlen_const("alse"))) {
+            goto unexpected_value;
+        }
+
+        /* 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);
+        break;
+
+    case 'n':
+        if (strncmp(jsonctx->in->current + 1, "ull", ly_strlen_const("ull"))) {
+            goto unexpected_value;
+        }
+
+        /* null */
+        lyjson_ctx_set_value(jsonctx, "", 0, 0);
+        ly_in_skip(jsonctx->in, ly_strlen_const("null"));
+        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_NULL);
+        break;
+
+    case ']':
+        if (!array_end) {
+            goto unexpected_value;
+        }
+
+        /* array end */
+        ly_in_skip(jsonctx->in, 1);
+        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_ARRAY_CLOSED);
+        break;
+
+    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);
+        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_CLOSED);
+        break;
+
+    case ',':
+        /* next object item */
+        ly_in_skip(jsonctx->in, 1);
+        LYJSON_STATUS_PUSH_RET(jsonctx, LYJSON_OBJECT_NEXT);
+        break;
+
+    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;
+    }
+
+    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 array-end or next item");
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lyjson_ctx_next(struct lyjson_ctx *jsonctx, enum LYJSON_PARSER_STATUS *status)
+{
+    LY_ERR ret = LY_SUCCESS;
+    enum LYJSON_PARSER_STATUS cur;
+
+    assert(jsonctx);
+
+    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;
+        }
+
+        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;
+    }
+
+    /* skip WS */
+    lyjson_skip_ws(jsonctx);
+
+cleanup:
+    if (!ret && status) {
+        *status = lyjson_ctx_status(jsonctx);
+    }
+    return ret;
+}
+
 void
 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;
 }
 
@@ -930,105 +1067,9 @@
     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)
 {