[njs] JSON object.
Dmitry Volyntsev
xeioex at nginx.com
Tue Oct 3 18:25:29 UTC 2017
details: http://hg.nginx.org/njs/rev/94c42736a730
branches:
changeset: 410:94c42736a730
user: Dmitry Volyntsev <xeioex at nginx.com>
date: Tue Oct 03 21:24:58 2017 +0300
description:
JSON object.
diffstat:
Makefile | 14 +
njs/njs_array.c | 2 +-
njs/njs_array.h | 1 +
njs/njs_builtin.c | 2 +
njs/njs_generator.c | 1 +
njs/njs_json.c | 2088 ++++++++++++++++++++++++++++++++++++++++++++++
njs/njs_json.h | 14 +
njs/njs_lexer_keyword.c | 1 +
njs/njs_number.c | 51 +-
njs/njs_number.h | 1 +
njs/njs_object.c | 42 +-
njs/njs_object.h | 2 +-
njs/njs_object_hash.h | 10 +
njs/njs_parser.c | 1 +
njs/njs_parser.h | 1 +
njs/njs_vm.h | 3 +-
njs/test/njs_unit_test.c | 589 ++++++++++++
nxt/nxt_utf8.h | 30 +
18 files changed, 2815 insertions(+), 38 deletions(-)
diffs (truncated from 3094 to 1000 lines):
diff -r f6b9efd315c5 -r 94c42736a730 Makefile
--- a/Makefile Tue Sep 26 14:19:49 2017 +0300
+++ b/Makefile Tue Oct 03 21:24:58 2017 +0300
@@ -16,6 +16,7 @@ NXT_BUILDDIR = build
$(NXT_BUILDDIR)/njs_string.o \
$(NXT_BUILDDIR)/njs_object.o \
$(NXT_BUILDDIR)/njs_array.o \
+ $(NXT_BUILDDIR)/njs_json.o \
$(NXT_BUILDDIR)/njs_function.o \
$(NXT_BUILDDIR)/njs_regexp.o \
$(NXT_BUILDDIR)/njs_date.o \
@@ -48,6 +49,7 @@ NXT_BUILDDIR = build
$(NXT_BUILDDIR)/njs_string.o \
$(NXT_BUILDDIR)/njs_object.o \
$(NXT_BUILDDIR)/njs_array.o \
+ $(NXT_BUILDDIR)/njs_json.o \
$(NXT_BUILDDIR)/njs_function.o \
$(NXT_BUILDDIR)/njs_regexp.o \
$(NXT_BUILDDIR)/njs_date.o \
@@ -211,6 +213,18 @@ dist:
-I$(NXT_LIB) -Injs \
njs/njs_array.c
+$(NXT_BUILDDIR)/njs_json.o: \
+ $(NXT_BUILDDIR)/libnxt.a \
+ njs/njscript.h \
+ njs/njs_vm.h \
+ njs/njs_object.h \
+ njs/njs_json.c \
+
+ $(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_json.o $(NXT_CFLAGS) \
+ -I$(NXT_LIB) -Injs \
+ njs/njs_json.c
+
+
$(NXT_BUILDDIR)/njs_function.o: \
$(NXT_BUILDDIR)/libnxt.a \
njs/njscript.h \
diff -r f6b9efd315c5 -r 94c42736a730 njs/njs_array.c
--- a/njs/njs_array.c Tue Sep 26 14:19:49 2017 +0300
+++ b/njs/njs_array.c Tue Oct 03 21:24:58 2017 +0300
@@ -157,7 +157,7 @@ njs_array_alloc(njs_vm_t *vm, uint32_t l
}
-static njs_ret_t
+njs_ret_t
njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value)
{
njs_ret_t ret;
diff -r f6b9efd315c5 -r 94c42736a730 njs/njs_array.h
--- a/njs/njs_array.h Tue Sep 26 14:19:49 2017 +0300
+++ b/njs/njs_array.h Tue Oct 03 21:24:58 2017 +0300
@@ -16,6 +16,7 @@
njs_array_t *njs_array_alloc(njs_vm_t *vm, uint32_t length, uint32_t spare);
+njs_ret_t njs_array_add(njs_vm_t *vm, njs_array_t *array, njs_value_t *value);
njs_ret_t njs_array_string_add(njs_vm_t *vm, njs_array_t *array, u_char *start,
size_t size, size_t length);
njs_ret_t njs_array_expand(njs_vm_t *vm, njs_array_t *array, uint32_t prepend,
diff -r f6b9efd315c5 -r 94c42736a730 njs/njs_builtin.c
--- a/njs/njs_builtin.c Tue Sep 26 14:19:49 2017 +0300
+++ b/njs/njs_builtin.c Tue Oct 03 21:24:58 2017 +0300
@@ -20,6 +20,7 @@
#include <njs_string.h>
#include <njs_object.h>
#include <njs_array.h>
+#include <njs_json.h>
#include <njs_function.h>
#include <njs_variable.h>
#include <njs_extern.h>
@@ -44,6 +45,7 @@ static nxt_int_t njs_builtin_completions
const njs_object_init_t *njs_object_init[] = {
NULL, /* global this */
&njs_math_object_init, /* Math */
+ &njs_json_object_init, /* JSON */
};
diff -r f6b9efd315c5 -r 94c42736a730 njs/njs_generator.c
--- a/njs/njs_generator.c Tue Sep 26 14:19:49 2017 +0300
+++ b/njs/njs_generator.c Tue Oct 03 21:24:58 2017 +0300
@@ -298,6 +298,7 @@ njs_generator(njs_vm_t *vm, njs_parser_t
case NJS_TOKEN_GLOBAL_THIS:
case NJS_TOKEN_MATH:
+ case NJS_TOKEN_JSON:
case NJS_TOKEN_EVAL:
case NJS_TOKEN_TO_STRING:
case NJS_TOKEN_IS_NAN:
diff -r f6b9efd315c5 -r 94c42736a730 njs/njs_json.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_json.c Tue Oct 03 21:24:58 2017 +0300
@@ -0,0 +1,2088 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_alignment.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_string.h>
+#include <njs_number.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_array.h>
+#include <njs_function.h>
+#include <stdio.h>
+#include <string.h>
+
+
+typedef struct {
+ njs_vm_t *vm;
+ nxt_mem_cache_pool_t *pool;
+ nxt_uint_t depth;
+ u_char *start;
+ u_char *end;
+} njs_json_parse_ctx_t;
+
+
+typedef struct {
+ njs_value_t value;
+
+ uint8_t written; /* 1 bit */
+
+ enum {
+ NJS_JSON_OBJECT_START,
+ NJS_JSON_OBJECT_CONTINUE,
+ NJS_JSON_OBJECT_TO_JSON_REPLACED,
+ NJS_JSON_OBJECT_REPLACED,
+ NJS_JSON_ARRAY_START,
+ NJS_JSON_ARRAY_CONTINUE,
+ NJS_JSON_ARRAY_TO_JSON_REPLACED,
+ NJS_JSON_ARRAY_REPLACED
+ } type:8;
+
+ uint32_t index;
+ njs_array_t *keys;
+ njs_value_t *prop_value;
+} njs_json_state_t;
+
+
+typedef struct {
+ union {
+ njs_continuation_t cont;
+ u_char padding[NJS_CONTINUATION_SIZE];
+ } u;
+ /*
+ * This retval value must be aligned so the continuation is padded
+ * to aligned size.
+ */
+ njs_value_t retval;
+
+ nxt_array_t stack;
+ njs_json_state_t *state;
+ njs_function_t *function;
+} njs_json_parse_t;
+
+
+typedef struct njs_chb_node_s njs_chb_node_t;
+
+struct njs_chb_node_s {
+ njs_chb_node_t *next;
+ u_char *start;
+ u_char *pos;
+ u_char *end;
+};
+
+
+typedef struct {
+ union {
+ njs_continuation_t cont;
+ u_char padding[NJS_CONTINUATION_SIZE];
+ } u;
+ /*
+ * This retval value must be aligned so the continuation is padded
+ * to aligned size.
+ */
+ njs_value_t retval;
+ njs_value_t key;
+
+ njs_vm_t *vm;
+ nxt_mem_cache_pool_t *pool;
+ njs_chb_node_t *nodes;
+ njs_chb_node_t *last;
+ nxt_array_t stack;
+ njs_json_state_t *state;
+
+ njs_value_t replacer;
+ nxt_str_t space;
+} njs_json_stringify_t;
+
+
+static u_char *njs_json_parse_value(njs_json_parse_ctx_t *ctx,
+ njs_value_t *value, u_char *p);
+static u_char *njs_json_parse_object(njs_json_parse_ctx_t *ctx,
+ njs_value_t *value, u_char *p);
+static u_char *njs_json_parse_array(njs_json_parse_ctx_t *ctx,
+ njs_value_t *value, u_char *p);
+static u_char *njs_json_parse_string(njs_json_parse_ctx_t *ctx,
+ njs_value_t *value, u_char *p);
+static u_char *njs_json_parse_number(njs_json_parse_ctx_t *ctx,
+ njs_value_t *value, u_char *p);
+nxt_inline uint32_t njs_json_unicode(const u_char *p);
+static u_char *njs_json_skip_space(u_char *start, u_char *end);
+
+static njs_ret_t njs_json_parse_continuation(njs_vm_t *vm,
+ njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_json_parse_continuation_apply(njs_vm_t *vm,
+ njs_json_parse_t *parse);
+static njs_json_state_t *njs_json_push_parse_state(njs_vm_t *vm,
+ njs_json_parse_t *parse, njs_value_t *value);
+static njs_json_state_t *njs_json_pop_parse_state(njs_json_parse_t *parse);
+static void njs_json_parse_exception(njs_json_parse_ctx_t *ctx,
+ const char* msg, u_char *pos);
+
+static njs_ret_t njs_json_stringify_continuation(njs_vm_t *vm,
+ njs_value_t *args, nxt_uint_t nargs, njs_index_t unused);
+static njs_function_t *njs_object_to_json_function(njs_vm_t *vm,
+ njs_value_t *value);
+static njs_ret_t njs_json_stringify_to_json(njs_vm_t *vm,
+ njs_json_stringify_t* stringify, njs_function_t *function,
+ njs_value_t *key, njs_value_t *value);
+static njs_ret_t njs_json_stringify_replacer(njs_vm_t *vm,
+ njs_json_stringify_t* stringify, njs_value_t *key, njs_value_t *value);
+static njs_ret_t njs_json_stringify_array(njs_vm_t *vm,
+ njs_json_stringify_t *stringify);
+static njs_json_state_t *njs_json_push_stringify_state(njs_vm_t *vm,
+ njs_json_stringify_t *stringify, njs_value_t *value);
+static njs_json_state_t *njs_json_pop_stringify_state(
+ njs_json_stringify_t *stringify);
+static void njs_json_stringify_exception(njs_json_stringify_t *stringify,
+ const char* msg);
+
+static nxt_int_t njs_json_append_value(njs_json_stringify_t *stringify,
+ njs_value_t *value);
+static nxt_int_t njs_json_append_string(njs_json_stringify_t *stringify,
+ njs_value_t *value);
+static nxt_int_t njs_json_append_number(njs_json_stringify_t *stringify,
+ njs_value_t *value);
+
+static njs_value_t *njs_json_wrap_value(njs_vm_t *vm, njs_value_t *value);
+
+
+#define NJS_JSON_BUF_MIN_SIZE 128
+
+#define njs_json_buf_written(stringify, bytes) \
+ (stringify)->last->pos += (bytes);
+
+#define njs_json_buf_node_size(n) (size_t) ((n)->pos - (n)->start)
+#define njs_json_buf_node_room(n) (size_t) ((n)->end - (n)->pos)
+
+static nxt_int_t njs_json_buf_append(njs_json_stringify_t *stringify,
+ const char* msg, size_t len);
+static u_char *njs_json_buf_reserve(njs_json_stringify_t *stringify,
+ size_t size);
+static nxt_int_t njs_json_buf_pullup(njs_json_stringify_t *stringify,
+ nxt_str_t *str);
+
+
+static njs_ret_t
+njs_json_parse(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+ njs_index_t unused)
+{
+ u_char *p, *end;
+ njs_value_t arg, *value, *wrapper;
+ njs_json_parse_t *parse;
+ njs_string_prop_t string;
+ njs_json_parse_ctx_t ctx;
+
+ value = nxt_mem_cache_alloc(vm->mem_cache_pool, sizeof(njs_value_t));
+ if (nxt_slow_path(value == NULL)) {
+ vm->exception = &njs_exception_memory_error;
+ return NXT_ERROR;
+ }
+
+ if (nargs < 2) {
+ arg = njs_string_void;
+ } else {
+ arg = args[1];
+ }
+
+ (void) njs_string_prop(&string, &arg);
+
+ p = string.start;
+ end = p + string.size;
+
+ ctx.vm = vm;
+ ctx.pool = vm->mem_cache_pool;
+ ctx.depth = 32;
+ ctx.start = string.start;
+ ctx.end = end;
+
+ p = njs_json_skip_space(p, end);
+ if (nxt_slow_path(p == end)) {
+ njs_json_parse_exception(&ctx, "Unexpected end of input", p);
+ return NXT_ERROR;
+ }
+
+ p = njs_json_parse_value(&ctx, value, p);
+ if (nxt_slow_path(p == NULL)) {
+ return NXT_ERROR;
+ }
+
+ p = njs_json_skip_space(p, end);
+ if (nxt_slow_path(p != end)) {
+ njs_json_parse_exception(&ctx, "Unexpected token", p);
+ return NXT_ERROR;
+ }
+
+ if (nargs >= 3 && njs_is_function(&args[2]) && njs_is_object(value)) {
+ wrapper = njs_json_wrap_value(vm, value);
+ if (nxt_slow_path(wrapper == NULL)) {
+ goto memory_error;
+ }
+
+ parse = njs_vm_continuation(vm);
+ parse->u.cont.function = njs_json_parse_continuation;
+ parse->function = args[2].data.u.function;
+
+ if (nxt_array_init(&parse->stack, NULL, 4, sizeof(njs_json_state_t),
+ &njs_array_mem_proto, vm->mem_cache_pool)
+ == NULL)
+ {
+ goto memory_error;
+ }
+
+ if (njs_json_push_parse_state(vm, parse, wrapper) == NULL) {
+ goto memory_error;
+ }
+
+ return njs_json_parse_continuation(vm, args, nargs, unused);
+ }
+
+ vm->retval = *value;
+
+ return NXT_OK;
+
+memory_error:
+
+ vm->exception = &njs_exception_memory_error;
+ return NXT_ERROR;
+}
+
+
+static njs_ret_t
+njs_json_stringify(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+ njs_index_t unused)
+{
+ double num;
+ nxt_int_t i;
+ njs_ret_t ret;
+ njs_value_t *wrapper;
+ njs_json_stringify_t *stringify;
+
+ if (nargs < 2) {
+ vm->retval = njs_value_void;
+ return NXT_OK;
+ }
+
+ stringify = njs_vm_continuation(vm);
+ stringify->vm = vm;
+ stringify->pool = vm->mem_cache_pool;
+ stringify->u.cont.function = njs_json_stringify_continuation;
+ stringify->nodes = NULL;
+ stringify->last = NULL;
+
+ if (nargs >= 3 && (njs_is_function(&args[2]) || njs_is_array(&args[2]))) {
+ stringify->replacer = args[2];
+ if (njs_is_array(&args[2])) {
+ ret = njs_json_stringify_array(vm, stringify);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto memory_error;
+ }
+ }
+
+ } else {
+ stringify->replacer = njs_value_void;
+ }
+
+ stringify->space.length = 0;
+
+ if (nargs >= 4 && (njs_is_string(&args[3]) || njs_is_number(&args[3]))) {
+ if (njs_is_string(&args[3])) {
+ njs_string_get(&args[3], &stringify->space);
+ stringify->space.length = nxt_min(stringify->space.length, 10);
+
+ } else {
+ num = args[3].data.u.number;
+ if (!isnan(num) && !isinf(num) && num > 0) {
+ num = nxt_min(num, 10);
+
+ stringify->space.length = (size_t) num;
+ stringify->space.start = nxt_mem_cache_alloc(vm->mem_cache_pool,
+ (size_t) num + 1);
+ if (nxt_slow_path(stringify->space.start == NULL)) {
+ goto memory_error;
+ }
+
+ for (i = 0; i < (int) num; i++) {
+ stringify->space.start[i] = ' ';
+ }
+ }
+ }
+ }
+
+ if (nxt_array_init(&stringify->stack, NULL, 4, sizeof(njs_json_state_t),
+ &njs_array_mem_proto, vm->mem_cache_pool)
+ == NULL)
+ {
+ goto memory_error;
+ }
+
+ wrapper = njs_json_wrap_value(vm, &args[1]);
+ if (nxt_slow_path(wrapper == NULL)) {
+ goto memory_error;
+ }
+
+ if (njs_json_push_stringify_state(vm, stringify, wrapper) == NULL) {
+ goto memory_error;
+ }
+
+ return njs_json_stringify_continuation(vm, args, nargs, unused);
+
+memory_error:
+
+ vm->exception = &njs_exception_memory_error;
+ return NXT_ERROR;
+}
+
+
+static u_char *
+njs_json_parse_value(njs_json_parse_ctx_t *ctx, njs_value_t *value, u_char *p)
+{
+ switch (*p) {
+ case '{':
+ return njs_json_parse_object(ctx, value, p);
+
+ case '[':
+ return njs_json_parse_array(ctx, value, p);
+
+ case '"':
+ return njs_json_parse_string(ctx, value, p);
+
+ case 't':
+ if (nxt_fast_path(ctx->end - p >= 4 && memcmp(p, "true", 4) == 0)) {
+ *value = njs_value_true;
+
+ return p + 4;
+ }
+
+ goto error;
+
+ case 'f':
+ if (nxt_fast_path(ctx->end - p >= 5 && memcmp(p, "false", 5) == 0)) {
+ *value = njs_value_false;
+
+ return p + 5;
+ }
+
+ goto error;
+
+ case 'n':
+ if (nxt_fast_path(ctx->end - p >= 4 && memcmp(p, "null", 4) == 0)) {
+ *value = njs_value_null;
+
+ return p + 4;
+ }
+
+ goto error;
+ }
+
+ if (nxt_fast_path(*p == '-' || (*p - '0') <= 9)) {
+ return njs_json_parse_number(ctx, value, p);
+ }
+
+error:
+
+ njs_json_parse_exception(ctx, "Unexpected token", p);
+
+ return NULL;
+}
+
+
+static u_char *
+njs_json_parse_object(njs_json_parse_ctx_t *ctx, njs_value_t *value, u_char *p)
+{
+ nxt_int_t ret;
+ njs_object_t *object;
+ njs_value_t *prop_name, *prop_value;
+ njs_object_prop_t *prop;
+ nxt_lvlhsh_query_t lhq;
+
+ if (nxt_slow_path(--ctx->depth == 0)) {
+ njs_json_parse_exception(ctx, "Nested too deep", p);
+ return NULL;
+ }
+
+ object = njs_object_alloc(ctx->vm);
+ if (nxt_slow_path(object == NULL)) {
+ goto memory_error;
+ }
+
+ prop = NULL;
+
+ for ( ;; ) {
+ p = njs_json_skip_space(p + 1, ctx->end);
+ if (nxt_slow_path(p == ctx->end)) {
+ goto error_end;
+ }
+
+ if (*p != '"') {
+ if (nxt_fast_path(*p == '}')) {
+ if (nxt_slow_path(prop != NULL)) {
+ njs_json_parse_exception(ctx, "Trailing comma", p - 1);
+ return NULL;
+ }
+
+ break;
+ }
+
+ goto error_token;
+ }
+
+ prop_name = nxt_mem_cache_alloc(ctx->pool, sizeof(njs_value_t));
+ if (nxt_slow_path(prop_name == NULL)) {
+ goto memory_error;
+ }
+
+ p = njs_json_parse_string(ctx, prop_name, p);
+ if (nxt_slow_path(p == NULL)) {
+ /* The exception is set by the called function. */
+ return NULL;
+ }
+
+ p = njs_json_skip_space(p, ctx->end);
+ if (nxt_slow_path(p == ctx->end || *p != ':')) {
+ goto error_token;
+ }
+
+ p = njs_json_skip_space(p + 1, ctx->end);
+ if (nxt_slow_path(p == ctx->end)) {
+ goto error_end;
+ }
+
+ prop_value = nxt_mem_cache_alloc(ctx->pool, sizeof(njs_value_t));
+ if (nxt_slow_path(prop_value == NULL)) {
+ goto memory_error;
+ }
+
+ p = njs_json_parse_value(ctx, prop_value, p);
+ if (nxt_slow_path(p == NULL)) {
+ /* The exception is set by the called function. */
+ return NULL;
+ }
+
+ prop = njs_object_prop_alloc(ctx->vm, prop_name, prop_value, 1);
+ if (nxt_slow_path(prop == NULL)) {
+ goto memory_error;
+ }
+
+ njs_string_get(prop_name, &lhq.key);
+ lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+ lhq.value = prop;
+ lhq.replace = 1;
+ lhq.pool = ctx->pool;
+ lhq.proto = &njs_object_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&object->hash, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ ctx->vm->exception = &njs_exception_internal_error;
+ return NULL;
+ }
+
+ p = njs_json_skip_space(p, ctx->end);
+ if (nxt_slow_path(p == ctx->end)) {
+ goto error_end;
+ }
+
+ if (*p != ',') {
+ if (nxt_fast_path(*p == '}')) {
+ break;
+ }
+
+ goto error_token;
+ }
+ }
+
+ value->data.u.object = object;
+ value->type = NJS_OBJECT;
+ value->data.truth = 1;
+
+ ctx->depth++;
+
+ return p + 1;
+
+error_token:
+
+ njs_json_parse_exception(ctx, "Unexpected token", p);
+
+ return NULL;
+
+error_end:
+
+ njs_json_parse_exception(ctx, "Unexpected end of input", p);
+
+ return NULL;
+
+memory_error:
+
+ ctx->vm->exception = &njs_exception_memory_error;
+
+ return NULL;
+}
+
+
+static u_char *
+njs_json_parse_array(njs_json_parse_ctx_t *ctx, njs_value_t *value, u_char *p)
+{
+ nxt_int_t ret;
+ njs_array_t *array;
+ njs_value_t *element;
+
+ if (nxt_slow_path(--ctx->depth == 0)) {
+ njs_json_parse_exception(ctx, "Nested too deep", p);
+ return NULL;
+ }
+
+ array = njs_array_alloc(ctx->vm, 0, 0);
+ if (nxt_slow_path(array == NULL)) {
+ ctx->vm->exception = &njs_exception_memory_error;
+ return NULL;
+ }
+
+ element = NULL;
+
+ for ( ;; ) {
+ p = njs_json_skip_space(p + 1, ctx->end);
+ if (nxt_slow_path(p == ctx->end)) {
+ goto error_end;
+ }
+
+ if (*p == ']') {
+ if (nxt_slow_path(element != NULL)) {
+ njs_json_parse_exception(ctx, "Trailing comma", p - 1);
+ return NULL;
+ }
+
+ break;
+ }
+
+ element = nxt_mem_cache_alloc(ctx->pool, sizeof(njs_value_t));
+ if (nxt_slow_path(element == NULL)) {
+ ctx->vm->exception = &njs_exception_memory_error;
+ return NULL;
+ }
+
+ p = njs_json_parse_value(ctx, element, p);
+ if (nxt_slow_path(p == NULL)) {
+ return NULL;
+ }
+
+ ret = njs_array_add(ctx->vm, array, element);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ ctx->vm->exception = &njs_exception_internal_error;
+ return NULL;
+ }
+
+ p = njs_json_skip_space(p, ctx->end);
+ if (nxt_slow_path(p == ctx->end)) {
+ goto error_end;
+ }
+
+ if (*p != ',') {
+ if (nxt_fast_path(*p == ']')) {
+ break;
+ }
+
+ goto error_token;
+ }
+ }
+
+ value->data.u.array = array;
+ value->type = NJS_ARRAY;
+ value->data.truth = 1;
+
+ ctx->depth++;
+
+ return p + 1;
+
+error_token:
+
+ njs_json_parse_exception(ctx, "Unexpected token", p);
+
+ return NULL;
+
+error_end:
+
+ njs_json_parse_exception(ctx, "Unexpected end of input", p);
+
+ return NULL;
+}
+
+
+static u_char *
+njs_json_parse_string(njs_json_parse_ctx_t *ctx, njs_value_t *value, u_char *p)
+{
+ u_char *s, ch, *last, *start;
+ size_t size, surplus;
+ ssize_t length;
+ uint32_t utf, utf_low;
+ njs_ret_t ret;
+
+ enum {
+ sw_usual = 0,
+ sw_escape,
+ sw_encoded1,
+ sw_encoded2,
+ sw_encoded3,
+ sw_encoded4,
+ } state;
+
+ start = p + 1;
+
+ state = 0;
+ surplus = 0;
+
+ for (p = start; p < ctx->end; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ case sw_usual:
+
+ if (ch == '"') {
+ break;
+ }
+
+ if (ch == '\\') {
+ state = sw_escape;
+ continue;
+ }
+
+ if (nxt_fast_path(ch >= ' ')) {
+ continue;
+ }
+
+ njs_json_parse_exception(ctx, "Forbidden source char", p);
+
+ return NULL;
+
+ case sw_escape:
+
+ switch (ch) {
+ case '"':
+ case '\\':
+ case '/':
+ case 'n':
+ case 'r':
+ case 't':
+ case 'b':
+ case 'f':
+ surplus++;
+ state = sw_usual;
+ continue;
+
+ case 'u':
+ /*
+ * Basic unicode 6 bytes "\uXXXX" in JSON
+ * and up to 3 bytes in UTF-8.
+ *
+ * Surrogate pair: 12 bytes "\uXXXX\uXXXX" in JSON
+ * and 3 or 4 bytes in UTF-8.
+ */
+ surplus += 3;
+ state = sw_encoded1;
+ continue;
+ }
+
+ njs_json_parse_exception(ctx, "Unknown escape char", p);
+
+ return NULL;
+
+ case sw_encoded1:
+ case sw_encoded2:
+ case sw_encoded3:
+ case sw_encoded4:
+
+ if (nxt_fast_path((ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch <= 'F')
+ || (ch >= 'a' && ch <= 'f')))
+ {
+ state = (state == sw_encoded4) ? sw_usual : state + 1;
+ continue;
+ }
+
+ njs_json_parse_exception(ctx, "Invalid Unicode escape sequence", p);
+
+ return NULL;
+ }
+
+ break;
+ }
+
+ if (nxt_slow_path(p == ctx->end)) {
+ njs_json_parse_exception(ctx, "Unexpected end of input", p);
+ return NULL;
+ }
+
+ /* Points to the ending quote mark. */
+ last = p;
+
+ size = last - start - surplus;
+
+ if (surplus != 0) {
+ p = start;
+
+ start = nxt_mem_cache_alloc(ctx->pool, size);
+ if (nxt_slow_path(start == NULL)) {
+ ctx->vm->exception = &njs_exception_memory_error;
+ return NULL;
+ }
+
+ s = start;
+
+ do {
+ ch = *p++;
+
+ if (ch != '\\') {
+ *s++ = ch;
+ continue;
+ }
+
+ ch = *p++;
+
+ switch (ch) {
+ case '"':
+ case '\\':
+ case '/':
+ *s++ = ch;
+ continue;
+
+ case 'n':
+ *s++ = '\n';
+ continue;
+
+ case 'r':
+ *s++ = '\r';
+ continue;
+
+ case 't':
+ *s++ = '\t';
+ continue;
+
+ case 'b':
+ *s++ = '\b';
+ continue;
+
+ case 'f':
+ *s++ = '\f';
+ continue;
+ }
+
+ /* "\uXXXX": Unicode escape sequence. */
+
+ utf = njs_json_unicode(p);
+ p += 4;
+
+ if (utf >= 0xd800 && utf <= 0xdfff) {
+
+ /* Surrogate pair. */
+
+ if (utf > 0xdbff || p[0] != '\\' || p[1] != 'u') {
+ njs_json_parse_exception(ctx, "Invalid Unicode char", p);
+ return NULL;
+ }
+
+ p += 2;
+
+ utf_low = njs_json_unicode(p);
+ p += 4;
+
+ if (nxt_slow_path(utf_low < 0xdc00 || utf_low > 0xdfff)) {
+ njs_json_parse_exception(ctx, "Invalid surrogate pair", p);
+ return NULL;
+ }
+
+ utf = 0x10000 + ((utf - 0xd800) << 10) + (utf_low - 0xdc00);
+ }
+
+ s = nxt_utf8_encode(s, utf);
+
+ } while (p != last);
+
+ size = s - start;
+ }
+
+ length = nxt_utf8_length(start, size);
+ if (nxt_slow_path(length < 0)) {
+ length = 0;
+ }
+
+ ret = njs_string_create(ctx->vm, value, start, size, length);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ ctx->vm->exception = &njs_exception_memory_error;
+ return NULL;
+ }
+
+ return last + 1;
+}
+
+
+static u_char *
+njs_json_parse_number(njs_json_parse_ctx_t *ctx, njs_value_t *value, u_char *p)
+{
+ u_char *start;
+ double num;
+ nxt_int_t sign;
+
+ sign = 1;
+
+ if (*p == '-') {
+ if (p + 1 == ctx->end) {
+ goto error;
+ }
+
+ p++;
+ sign = -1;
+ }
+
+ start = p;
+ num = njs_number_dec_parse(&p, ctx->end);
+ if (p != start) {
+ *value = njs_value_zero;
+ value->data.u.number = sign * num;
+
+ return p;
+ }
+
+error:
+
+ njs_json_parse_exception(ctx, "Unexpected number", p);
+
+ return NULL;
+}
+
+
+nxt_inline uint32_t
+njs_json_unicode(const u_char *p)
+{
+ u_char c;
+ uint32_t utf;
+ nxt_uint_t i;
+
+ utf = 0;
+
+ for (i = 0; i < 4; i++) {
+ utf <<= 4;
+ c = p[i] | 0x20;
+ c -= '0';
+ if (c > 9) {
+ c += '0' - 'a' + 10;
+ }
+
+ utf |= c;
+ }
+
+ return utf;
+}
+
+
+static u_char *
+njs_json_skip_space(u_char *start, u_char *end)
+{
+ u_char *p;
+
+ for (p = start; nxt_fast_path(p != end); p++) {
+
+ switch (*p) {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ continue;
+ }
+
+ break;
More information about the nginx-devel
mailing list