details: http://hg.nginx.org/njs/rev/94c42736a730 branches: changeset: 410:94c42736a730 user: Dmitry Volyntsev <xei...@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; _______________________________________________ nginx-devel mailing list nginx-devel@nginx.org http://mailman.nginx.org/mailman/listinfo/nginx-devel