From f23078cbd302d32649537eedda1769c5685228d8 Mon Sep 17 00:00:00 2001 From: Craig Tiller Date: Fri, 25 Mar 2016 17:07:29 -0700 Subject: Stage #1 of core breakup: move everything under lib --- src/core/lib/json/json.c | 64 ++++ src/core/lib/json/json.h | 88 ++++++ src/core/lib/json/json_common.h | 49 +++ src/core/lib/json/json_reader.c | 659 ++++++++++++++++++++++++++++++++++++++++ src/core/lib/json/json_reader.h | 160 ++++++++++ src/core/lib/json/json_string.c | 379 +++++++++++++++++++++++ src/core/lib/json/json_writer.c | 258 ++++++++++++++++ src/core/lib/json/json_writer.h | 97 ++++++ 8 files changed, 1754 insertions(+) create mode 100644 src/core/lib/json/json.c create mode 100644 src/core/lib/json/json.h create mode 100644 src/core/lib/json/json_common.h create mode 100644 src/core/lib/json/json_reader.c create mode 100644 src/core/lib/json/json_reader.h create mode 100644 src/core/lib/json/json_string.c create mode 100644 src/core/lib/json/json_writer.c create mode 100644 src/core/lib/json/json_writer.h (limited to 'src/core/lib/json') diff --git a/src/core/lib/json/json.c b/src/core/lib/json/json.c new file mode 100644 index 0000000000..96e11eebb1 --- /dev/null +++ b/src/core/lib/json/json.c @@ -0,0 +1,64 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include + +#include "src/core/json/json.h" + +grpc_json *grpc_json_create(grpc_json_type type) { + grpc_json *json = gpr_malloc(sizeof(*json)); + memset(json, 0, sizeof(*json)); + json->type = type; + + return json; +} + +void grpc_json_destroy(grpc_json *json) { + while (json->child) { + grpc_json_destroy(json->child); + } + + if (json->next) { + json->next->prev = json->prev; + } + + if (json->prev) { + json->prev->next = json->next; + } else if (json->parent) { + json->parent->child = json->next; + } + + gpr_free(json); +} diff --git a/src/core/lib/json/json.h b/src/core/lib/json/json.h new file mode 100644 index 0000000000..aea9d5dadb --- /dev/null +++ b/src/core/lib/json/json.h @@ -0,0 +1,88 @@ +/* + * + * Copyright 2015-2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_CORE_JSON_JSON_H +#define GRPC_CORE_JSON_JSON_H + +#include + +#include "src/core/json/json_common.h" + +/* A tree-like structure to hold json values. The key and value pointers + * are not owned by it. + */ +typedef struct grpc_json { + struct grpc_json *next; + struct grpc_json *prev; + struct grpc_json *child; + struct grpc_json *parent; + + grpc_json_type type; + const char *key; + const char *value; +} grpc_json; + +/* The next two functions are going to parse the input string, and + * modify it in the process, in order to use its space to store + * all of the keys and values for the returned object tree. + * + * They assume UTF-8 input stream, and will output UTF-8 encoded + * strings in the tree. The input stream's UTF-8 isn't validated, + * as in, what you input is what you get as an output. + * + * All the keys and values in the grpc_json objects will be strings + * pointing at your input buffer. + * + * Delete the allocated tree afterward using grpc_json_destroy(). + */ +grpc_json *grpc_json_parse_string_with_len(char *input, size_t size); +grpc_json *grpc_json_parse_string(char *input); + +/* This function will create a new string using gpr_realloc, and will + * deserialize the grpc_json tree into it. It'll be zero-terminated, + * but will be allocated in chunks of 256 bytes. + * + * The indent parameter controls the way the output is formatted. + * If indent is 0, then newlines will be suppressed as well, and the + * output will be condensed at its maximum. + */ +char *grpc_json_dump_to_string(grpc_json *json, int indent); + +/* Use these to create or delete a grpc_json object. + * Deletion is recursive. We will not attempt to free any of the strings + * in any of the objects of that tree. + */ +grpc_json *grpc_json_create(grpc_json_type type); +void grpc_json_destroy(grpc_json *json); + +#endif /* GRPC_CORE_JSON_JSON_H */ diff --git a/src/core/lib/json/json_common.h b/src/core/lib/json/json_common.h new file mode 100644 index 0000000000..7205a94685 --- /dev/null +++ b/src/core/lib/json/json_common.h @@ -0,0 +1,49 @@ +/* + * + * Copyright 2015-2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_CORE_JSON_JSON_COMMON_H +#define GRPC_CORE_JSON_JSON_COMMON_H + +/* The various json types. */ +typedef enum { + GRPC_JSON_OBJECT, + GRPC_JSON_ARRAY, + GRPC_JSON_STRING, + GRPC_JSON_NUMBER, + GRPC_JSON_TRUE, + GRPC_JSON_FALSE, + GRPC_JSON_NULL, + GRPC_JSON_TOP_LEVEL +} grpc_json_type; + +#endif /* GRPC_CORE_JSON_JSON_COMMON_H */ diff --git a/src/core/lib/json/json_reader.c b/src/core/lib/json/json_reader.c new file mode 100644 index 0000000000..30da6f28f3 --- /dev/null +++ b/src/core/lib/json/json_reader.c @@ -0,0 +1,659 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include + +#include + +#include "src/core/json/json_reader.h" + +static void json_reader_string_clear(grpc_json_reader *reader) { + reader->vtable->string_clear(reader->userdata); +} + +static void json_reader_string_add_char(grpc_json_reader *reader, uint32_t c) { + reader->vtable->string_add_char(reader->userdata, c); +} + +static void json_reader_string_add_utf32(grpc_json_reader *reader, + uint32_t utf32) { + reader->vtable->string_add_utf32(reader->userdata, utf32); +} + +static uint32_t grpc_json_reader_read_char(grpc_json_reader *reader) { + return reader->vtable->read_char(reader->userdata); +} + +static void json_reader_container_begins(grpc_json_reader *reader, + grpc_json_type type) { + reader->vtable->container_begins(reader->userdata, type); +} + +static grpc_json_type grpc_json_reader_container_ends( + grpc_json_reader *reader) { + return reader->vtable->container_ends(reader->userdata); +} + +static void json_reader_set_key(grpc_json_reader *reader) { + reader->vtable->set_key(reader->userdata); +} + +static void json_reader_set_string(grpc_json_reader *reader) { + reader->vtable->set_string(reader->userdata); +} + +static int json_reader_set_number(grpc_json_reader *reader) { + return reader->vtable->set_number(reader->userdata); +} + +static void json_reader_set_true(grpc_json_reader *reader) { + reader->vtable->set_true(reader->userdata); +} + +static void json_reader_set_false(grpc_json_reader *reader) { + reader->vtable->set_false(reader->userdata); +} + +static void json_reader_set_null(grpc_json_reader *reader) { + reader->vtable->set_null(reader->userdata); +} + +/* Call this function to initialize the reader structure. */ +void grpc_json_reader_init(grpc_json_reader *reader, + grpc_json_reader_vtable *vtable, void *userdata) { + memset(reader, 0, sizeof(*reader)); + reader->vtable = vtable; + reader->userdata = userdata; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; +} + +int grpc_json_reader_is_complete(grpc_json_reader *reader) { + return ((reader->depth == 0) && + ((reader->state == GRPC_JSON_STATE_END) || + (reader->state == GRPC_JSON_STATE_VALUE_END))); +} + +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader) { + uint32_t c, success; + + /* This state-machine is a strict implementation of ECMA-404 */ + for (;;) { + c = grpc_json_reader_read_char(reader); + switch (c) { + /* Let's process the error cases first. */ + case GRPC_JSON_READ_CHAR_ERROR: + return GRPC_JSON_READ_ERROR; + + case GRPC_JSON_READ_CHAR_EAGAIN: + return GRPC_JSON_EAGAIN; + + case GRPC_JSON_READ_CHAR_EOF: + if (grpc_json_reader_is_complete(reader)) { + return GRPC_JSON_DONE; + } else { + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Processing whitespaces. */ + case ' ': + case '\t': + case '\n': + case '\r': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_OBJECT_KEY_END: + case GRPC_JSON_STATE_VALUE_BEGIN: + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_END: + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + if (c != ' ') return GRPC_JSON_PARSE_ERROR; + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = (uint32_t)json_reader_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* Value, object or array terminations. */ + case ',': + case '}': + case ']': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + success = (uint32_t)json_reader_set_number(reader); + if (!success) return GRPC_JSON_PARSE_ERROR; + json_reader_string_clear(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + /* The missing break here is intentional. */ + + case GRPC_JSON_STATE_VALUE_END: + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + case GRPC_JSON_STATE_VALUE_BEGIN: + if (c == ',') { + if (reader->state != GRPC_JSON_STATE_VALUE_END) { + return GRPC_JSON_PARSE_ERROR; + } + if (reader->in_object) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + } else { + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + } + } else { + if (reader->depth-- == 0) return GRPC_JSON_PARSE_ERROR; + if ((c == '}') && !reader->in_object) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == '}') && + (reader->state == GRPC_JSON_STATE_OBJECT_KEY_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + if ((c == ']') && !reader->in_array) return GRPC_JSON_PARSE_ERROR; + if ((c == ']') && + (reader->state == GRPC_JSON_STATE_VALUE_BEGIN) && + !reader->container_just_begun) { + return GRPC_JSON_PARSE_ERROR; + } + reader->state = GRPC_JSON_STATE_VALUE_END; + switch (grpc_json_reader_container_ends(reader)) { + case GRPC_JSON_OBJECT: + reader->in_object = 1; + reader->in_array = 0; + break; + case GRPC_JSON_ARRAY: + reader->in_object = 0; + reader->in_array = 1; + break; + case GRPC_JSON_TOP_LEVEL: + GPR_ASSERT(reader->depth == 0); + reader->in_object = 0; + reader->in_array = 0; + reader->state = GRPC_JSON_STATE_END; + break; + default: + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); + } + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + /* In-string escaping. */ + case '\\': + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + reader->escaped_string_was_key = 1; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + case GRPC_JSON_STATE_VALUE_STRING: + reader->escaped_string_was_key = 0; + reader->state = GRPC_JSON_STATE_STRING_ESCAPE; + break; + + /* This is the \\ case. */ + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, '\\'); + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + default: + reader->container_just_begun = 0; + switch (reader->state) { + case GRPC_JSON_STATE_OBJECT_KEY_BEGIN: + if (c != '"') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + break; + + case GRPC_JSON_STATE_OBJECT_KEY_STRING: + GPR_ASSERT(reader->unicode_high_surrogate == 0); + if (c == '"') { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; + json_reader_set_key(reader); + json_reader_string_clear(reader); + } else { + if (c <= 0x001f) return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + if (c == '"') { + reader->state = GRPC_JSON_STATE_VALUE_END; + json_reader_set_string(reader); + json_reader_string_clear(reader); + } else { + if (c < 32) return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + } + break; + + case GRPC_JSON_STATE_OBJECT_KEY_END: + if (c != ':') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_BEGIN; + break; + + case GRPC_JSON_STATE_VALUE_BEGIN: + switch (c) { + case 't': + reader->state = GRPC_JSON_STATE_VALUE_TRUE_R; + break; + + case 'f': + reader->state = GRPC_JSON_STATE_VALUE_FALSE_A; + break; + + case 'n': + reader->state = GRPC_JSON_STATE_VALUE_NULL_U; + break; + + case '"': + reader->state = GRPC_JSON_STATE_VALUE_STRING; + break; + + case '0': + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_ZERO; + break; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER; + break; + + case '{': + reader->container_just_begun = 1; + json_reader_container_begins(reader, GRPC_JSON_OBJECT); + reader->depth++; + reader->state = GRPC_JSON_STATE_OBJECT_KEY_BEGIN; + reader->in_object = 1; + reader->in_array = 0; + break; + + case '[': + reader->container_just_begun = 1; + json_reader_container_begins(reader, GRPC_JSON_ARRAY); + reader->depth++; + reader->in_object = 0; + reader->in_array = 1; + break; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + if (reader->unicode_high_surrogate && c != 'u') + return GRPC_JSON_PARSE_ERROR; + switch (c) { + case '"': + case '/': + json_reader_string_add_char(reader, c); + break; + case 'b': + json_reader_string_add_char(reader, '\b'); + break; + case 'f': + json_reader_string_add_char(reader, '\f'); + break; + case 'n': + json_reader_string_add_char(reader, '\n'); + break; + case 'r': + json_reader_string_add_char(reader, '\r'); + break; + case 't': + json_reader_string_add_char(reader, '\t'); + break; + case 'u': + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; + reader->unicode_char = 0; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + if ((c >= '0') && (c <= '9')) { + c -= '0'; + } else if ((c >= 'A') && (c <= 'F')) { + c -= 'A' - 10; + } else if ((c >= 'a') && (c <= 'f')) { + c -= 'a' - 10; + } else { + return GRPC_JSON_PARSE_ERROR; + } + reader->unicode_char = (uint16_t)(reader->unicode_char << 4); + reader->unicode_char = (uint16_t)(reader->unicode_char | c); + + switch (reader->state) { + case GRPC_JSON_STATE_STRING_ESCAPE_U1: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U2; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U2: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U3; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U3: + reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; + break; + case GRPC_JSON_STATE_STRING_ESCAPE_U4: + /* See grpc_json_writer_escape_string to have a description + * of what's going on here. + */ + if ((reader->unicode_char & 0xfc00) == 0xd800) { + /* high surrogate utf-16 */ + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + reader->unicode_high_surrogate = reader->unicode_char; + } else if ((reader->unicode_char & 0xfc00) == 0xdc00) { + /* low surrogate utf-16 */ + uint32_t utf32; + if (reader->unicode_high_surrogate == 0) + return GRPC_JSON_PARSE_ERROR; + utf32 = 0x10000; + utf32 += (uint32_t)( + (reader->unicode_high_surrogate - 0xd800) * 0x400); + utf32 += (uint32_t)(reader->unicode_char - 0xdc00); + json_reader_string_add_utf32(reader, utf32); + reader->unicode_high_surrogate = 0; + } else { + /* anything else */ + if (reader->unicode_high_surrogate != 0) + return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_utf32(reader, reader->unicode_char); + } + if (reader->escaped_string_was_key) { + reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; + } else { + reader->state = GRPC_JSON_STATE_VALUE_STRING; + } + break; + default: + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + case '.': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + case 'e': + case 'E': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_E; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: + if (c != '.') return GRPC_JSON_PARSE_ERROR; + json_reader_string_add_char(reader, c); + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_DOT; + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_DOT: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_E: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + reader->state = GRPC_JSON_STATE_VALUE_NUMBER_EPM; + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_NUMBER_EPM: + json_reader_string_add_char(reader, c); + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + break; + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_VALUE_TRUE_R: + if (c != 'r') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_U; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_TRUE_E; + break; + + case GRPC_JSON_STATE_VALUE_TRUE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + json_reader_set_true(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_A: + if (c != 'a') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_L; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_L: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_S; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_S: + if (c != 's') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_FALSE_E; + break; + + case GRPC_JSON_STATE_VALUE_FALSE_E: + if (c != 'e') return GRPC_JSON_PARSE_ERROR; + json_reader_set_false(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + case GRPC_JSON_STATE_VALUE_NULL_U: + if (c != 'u') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L1; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L1: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + reader->state = GRPC_JSON_STATE_VALUE_NULL_L2; + break; + + case GRPC_JSON_STATE_VALUE_NULL_L2: + if (c != 'l') return GRPC_JSON_PARSE_ERROR; + json_reader_set_null(reader); + reader->state = GRPC_JSON_STATE_VALUE_END; + break; + + /* All of the VALUE_END cases are handled in the specialized case + * above. */ + case GRPC_JSON_STATE_VALUE_END: + switch (c) { + case ',': + case '}': + case ']': + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); + break; + + default: + return GRPC_JSON_PARSE_ERROR; + } + break; + + case GRPC_JSON_STATE_END: + return GRPC_JSON_PARSE_ERROR; + } + } + } + + GPR_UNREACHABLE_CODE(return GRPC_JSON_INTERNAL_ERROR); +} diff --git a/src/core/lib/json/json_reader.h b/src/core/lib/json/json_reader.h new file mode 100644 index 0000000000..f25f44b2ef --- /dev/null +++ b/src/core/lib/json/json_reader.h @@ -0,0 +1,160 @@ +/* + * + * Copyright 2015-2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef GRPC_CORE_JSON_JSON_READER_H +#define GRPC_CORE_JSON_JSON_READER_H + +#include +#include "src/core/json/json_common.h" + +typedef enum { + GRPC_JSON_STATE_OBJECT_KEY_BEGIN, + GRPC_JSON_STATE_OBJECT_KEY_STRING, + GRPC_JSON_STATE_OBJECT_KEY_END, + GRPC_JSON_STATE_VALUE_BEGIN, + GRPC_JSON_STATE_VALUE_STRING, + GRPC_JSON_STATE_STRING_ESCAPE, + GRPC_JSON_STATE_STRING_ESCAPE_U1, + GRPC_JSON_STATE_STRING_ESCAPE_U2, + GRPC_JSON_STATE_STRING_ESCAPE_U3, + GRPC_JSON_STATE_STRING_ESCAPE_U4, + GRPC_JSON_STATE_VALUE_NUMBER, + GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL, + GRPC_JSON_STATE_VALUE_NUMBER_ZERO, + GRPC_JSON_STATE_VALUE_NUMBER_DOT, + GRPC_JSON_STATE_VALUE_NUMBER_E, + GRPC_JSON_STATE_VALUE_NUMBER_EPM, + GRPC_JSON_STATE_VALUE_TRUE_R, + GRPC_JSON_STATE_VALUE_TRUE_U, + GRPC_JSON_STATE_VALUE_TRUE_E, + GRPC_JSON_STATE_VALUE_FALSE_A, + GRPC_JSON_STATE_VALUE_FALSE_L, + GRPC_JSON_STATE_VALUE_FALSE_S, + GRPC_JSON_STATE_VALUE_FALSE_E, + GRPC_JSON_STATE_VALUE_NULL_U, + GRPC_JSON_STATE_VALUE_NULL_L1, + GRPC_JSON_STATE_VALUE_NULL_L2, + GRPC_JSON_STATE_VALUE_END, + GRPC_JSON_STATE_END +} grpc_json_reader_state; + +enum { + /* The first non-unicode value is 0x110000. But let's pick + * a value high enough to start our error codes from. These + * values are safe to return from the read_char function. + */ + GRPC_JSON_READ_CHAR_EOF = 0x7ffffff0, + GRPC_JSON_READ_CHAR_EAGAIN, + GRPC_JSON_READ_CHAR_ERROR +}; + +struct grpc_json_reader; + +typedef struct grpc_json_reader_vtable { + /* Clears your internal string scratchpad. */ + void (*string_clear)(void *userdata); + /* Adds a char to the string scratchpad. */ + void (*string_add_char)(void *userdata, uint32_t c); + /* Adds a utf32 char to the string scratchpad. */ + void (*string_add_utf32)(void *userdata, uint32_t c); + /* Reads a character from your input. May be utf-8, 16 or 32. */ + uint32_t (*read_char)(void *userdata); + /* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */ + void (*container_begins)(void *userdata, grpc_json_type type); + /* Ends the current container. Must return the type of its parent. */ + grpc_json_type (*container_ends)(void *userdata); + /* Your internal string scratchpad is an object's key. */ + void (*set_key)(void *userdata); + /* Your internal string scratchpad is a string value. */ + void (*set_string)(void *userdata); + /* Your internal string scratchpad is a numerical value. Return 1 if valid. */ + int (*set_number)(void *userdata); + /* Sets the values true, false or null. */ + void (*set_true)(void *userdata); + void (*set_false)(void *userdata); + void (*set_null)(void *userdata); +} grpc_json_reader_vtable; + +typedef struct grpc_json_reader { + /* That structure is fully private, and initialized by grpc_json_reader_init. + * The definition is public so you can put it on your stack. + */ + + void *userdata; + grpc_json_reader_vtable *vtable; + int depth; + int in_object; + int in_array; + int escaped_string_was_key; + int container_just_begun; + uint16_t unicode_char, unicode_high_surrogate; + grpc_json_reader_state state; +} grpc_json_reader; + +/* The return type of the parser. */ +typedef enum { + GRPC_JSON_DONE, /* The parser finished successfully. */ + GRPC_JSON_EAGAIN, /* The parser yields to get more data. */ + GRPC_JSON_READ_ERROR, /* The parser passes through a read error. */ + GRPC_JSON_PARSE_ERROR, /* The parser found an error in the json stream. */ + GRPC_JSON_INTERNAL_ERROR /* The parser got an internal error. */ +} grpc_json_reader_status; + +/* Call this function to start parsing the input. It will return the following: + * . GRPC_JSON_DONE if the input got eof, and the parsing finished + * successfully. + * . GRPC_JSON_EAGAIN if the read_char function returned again. Call the + * parser again as needed. It is okay to call the parser in polling mode, + * although a bit dull. + * . GRPC_JSON_READ_ERROR if the read_char function returned an error. The + * state isn't broken however, and the function can be called again if the + * error has been corrected. But please use the EAGAIN feature instead for + * consistency. + * . GRPC_JSON_PARSE_ERROR if the input was somehow invalid. + * . GRPC_JSON_INTERNAL_ERROR if the parser somehow ended into an invalid + * internal state. + */ +grpc_json_reader_status grpc_json_reader_run(grpc_json_reader *reader); + +/* Call this function to initialize the reader structure. */ +void grpc_json_reader_init(grpc_json_reader *reader, + grpc_json_reader_vtable *vtable, void *userdata); + +/* You may call this from the read_char callback if you don't know where is the + * end of your input stream, and you'd like the json reader to hint you that it + * has completed reading its input, so you can return an EOF to it. Note that + * there might still be trailing whitespaces after that point. + */ +int grpc_json_reader_is_complete(grpc_json_reader *reader); + +#endif /* GRPC_CORE_JSON_JSON_READER_H */ diff --git a/src/core/lib/json/json_string.c b/src/core/lib/json/json_string.c new file mode 100644 index 0000000000..d4ebce18e1 --- /dev/null +++ b/src/core/lib/json/json_string.c @@ -0,0 +1,379 @@ +/* + * + * Copyright 2015-2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include +#include + +#include +#include + +#include "src/core/json/json.h" +#include "src/core/json/json_reader.h" +#include "src/core/json/json_writer.h" + +/* The json reader will construct a bunch of grpc_json objects and + * link them all up together in a tree-like structure that will represent + * the json data in memory. + * + * It also uses its own input as a scratchpad to store all of the decoded, + * unescaped strings. So we need to keep track of all these pointers in + * that opaque structure the reader will carry for us. + * + * Note that this works because the act of parsing json always reduces its + * input size, and never expands it. + */ +typedef struct { + grpc_json *top; + grpc_json *current_container; + grpc_json *current_value; + uint8_t *input; + uint8_t *key; + uint8_t *string; + uint8_t *string_ptr; + size_t remaining_input; +} json_reader_userdata; + +/* This json writer will put everything in a big string. + * The point is that we allocate that string in chunks of 256 bytes. + */ +typedef struct { + char *output; + size_t free_space; + size_t string_len; + size_t allocated; +} json_writer_userdata; + +/* This function checks if there's enough space left in the output buffer, + * and will enlarge it if necessary. We're only allocating chunks of 256 + * bytes at a time (or multiples thereof). + */ +static void json_writer_output_check(void *userdata, size_t needed) { + json_writer_userdata *state = userdata; + if (state->free_space >= needed) return; + needed -= state->free_space; + /* Round up by 256 bytes. */ + needed = (needed + 0xff) & ~0xffU; + state->output = gpr_realloc(state->output, state->allocated + needed); + state->free_space += needed; + state->allocated += needed; +} + +/* These are needed by the writer's implementation. */ +static void json_writer_output_char(void *userdata, char c) { + json_writer_userdata *state = userdata; + json_writer_output_check(userdata, 1); + state->output[state->string_len++] = c; + state->free_space--; +} + +static void json_writer_output_string_with_len(void *userdata, const char *str, + size_t len) { + json_writer_userdata *state = userdata; + json_writer_output_check(userdata, len); + memcpy(state->output + state->string_len, str, len); + state->string_len += len; + state->free_space -= len; +} + +static void json_writer_output_string(void *userdata, const char *str) { + size_t len = strlen(str); + json_writer_output_string_with_len(userdata, str, len); +} + +/* The reader asks us to clear our scratchpad. In our case, we'll simply mark + * the end of the current string, and advance our output pointer. + */ +static void json_reader_string_clear(void *userdata) { + json_reader_userdata *state = userdata; + if (state->string) { + GPR_ASSERT(state->string_ptr < state->input); + *state->string_ptr++ = 0; + } + state->string = state->string_ptr; +} + +static void json_reader_string_add_char(void *userdata, uint32_t c) { + json_reader_userdata *state = userdata; + GPR_ASSERT(state->string_ptr < state->input); + GPR_ASSERT(c <= 0xff); + *state->string_ptr++ = (uint8_t)c; +} + +/* We are converting a UTF-32 character into UTF-8 here, + * as described by RFC3629. + */ +static void json_reader_string_add_utf32(void *userdata, uint32_t c) { + if (c <= 0x7f) { + json_reader_string_add_char(userdata, c); + } else if (c <= 0x7ff) { + uint32_t b1 = 0xc0 | ((c >> 6) & 0x1f); + uint32_t b2 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + } else if (c <= 0xffff) { + uint32_t b1 = 0xe0 | ((c >> 12) & 0x0f); + uint32_t b2 = 0x80 | ((c >> 6) & 0x3f); + uint32_t b3 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + json_reader_string_add_char(userdata, b3); + } else if (c <= 0x1fffff) { + uint32_t b1 = 0xf0 | ((c >> 18) & 0x07); + uint32_t b2 = 0x80 | ((c >> 12) & 0x3f); + uint32_t b3 = 0x80 | ((c >> 6) & 0x3f); + uint32_t b4 = 0x80 | (c & 0x3f); + json_reader_string_add_char(userdata, b1); + json_reader_string_add_char(userdata, b2); + json_reader_string_add_char(userdata, b3); + json_reader_string_add_char(userdata, b4); + } +} + +/* We consider that the input may be a zero-terminated string. So we + * can end up hitting eof before the end of the alleged string length. + */ +static uint32_t json_reader_read_char(void *userdata) { + uint32_t r; + json_reader_userdata *state = userdata; + + if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; + + r = *state->input++; + state->remaining_input--; + + if (r == 0) { + state->remaining_input = 0; + return GRPC_JSON_READ_CHAR_EOF; + } + + return r; +} + +/* Helper function to create a new grpc_json object and link it into + * our tree-in-progress inside our opaque structure. + */ +static grpc_json *json_create_and_link(void *userdata, grpc_json_type type) { + json_reader_userdata *state = userdata; + grpc_json *json = grpc_json_create(type); + + json->parent = state->current_container; + json->prev = state->current_value; + state->current_value = json; + + if (json->prev) { + json->prev->next = json; + } + if (json->parent) { + if (!json->parent->child) { + json->parent->child = json; + } + if (json->parent->type == GRPC_JSON_OBJECT) { + json->key = (char *)state->key; + } + } + if (!state->top) { + state->top = json; + } + + return json; +} + +static void json_reader_container_begins(void *userdata, grpc_json_type type) { + json_reader_userdata *state = userdata; + grpc_json *container; + + GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); + + container = json_create_and_link(userdata, type); + state->current_container = container; + state->current_value = NULL; +} + +/* It's important to remember that the reader is mostly stateless, so it + * isn't trying to remember what the container was prior the one that just + * ends. Since we're keeping track of these for our own purpose, we are + * able to return that information back, which is useful for it to validate + * the input json stream. + * + * Also note that if we're at the top of the tree, and the last container + * ends, we have to return GRPC_JSON_TOP_LEVEL. + */ +static grpc_json_type json_reader_container_ends(void *userdata) { + grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; + json_reader_userdata *state = userdata; + + GPR_ASSERT(state->current_container); + + state->current_value = state->current_container; + state->current_container = state->current_container->parent; + + if (state->current_container) { + container_type = state->current_container->type; + } + + return container_type; +} + +/* The next 3 functions basically are the reader asking us to use our string + * scratchpad for one of these 3 purposes. + * + * Note that in the set_number case, we're not going to try interpreting it. + * We'll keep it as a string, and leave it to the caller to evaluate it. + */ +static void json_reader_set_key(void *userdata) { + json_reader_userdata *state = userdata; + state->key = state->string; +} + +static void json_reader_set_string(void *userdata) { + json_reader_userdata *state = userdata; + grpc_json *json = json_create_and_link(userdata, GRPC_JSON_STRING); + json->value = (char *)state->string; +} + +static int json_reader_set_number(void *userdata) { + json_reader_userdata *state = userdata; + grpc_json *json = json_create_and_link(userdata, GRPC_JSON_NUMBER); + json->value = (char *)state->string; + return 1; +} + +/* The object types true, false and null are self-sufficient, and don't need + * any more information beside their type. + */ +static void json_reader_set_true(void *userdata) { + json_create_and_link(userdata, GRPC_JSON_TRUE); +} + +static void json_reader_set_false(void *userdata) { + json_create_and_link(userdata, GRPC_JSON_FALSE); +} + +static void json_reader_set_null(void *userdata) { + json_create_and_link(userdata, GRPC_JSON_NULL); +} + +static grpc_json_reader_vtable reader_vtable = { + json_reader_string_clear, json_reader_string_add_char, + json_reader_string_add_utf32, json_reader_read_char, + json_reader_container_begins, json_reader_container_ends, + json_reader_set_key, json_reader_set_string, + json_reader_set_number, json_reader_set_true, + json_reader_set_false, json_reader_set_null}; + +/* And finally, let's define our public API. */ +grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { + grpc_json_reader reader; + json_reader_userdata state; + grpc_json *json = NULL; + grpc_json_reader_status status; + + if (!input) return NULL; + + state.top = state.current_container = state.current_value = NULL; + state.string = state.key = NULL; + state.string_ptr = state.input = (uint8_t *)input; + state.remaining_input = size; + grpc_json_reader_init(&reader, &reader_vtable, &state); + + status = grpc_json_reader_run(&reader); + json = state.top; + + if ((status != GRPC_JSON_DONE) && json) { + grpc_json_destroy(json); + json = NULL; + } + + return json; +} + +#define UNBOUND_JSON_STRING_LENGTH 0x7fffffff + +grpc_json *grpc_json_parse_string(char *input) { + return grpc_json_parse_string_with_len(input, UNBOUND_JSON_STRING_LENGTH); +} + +static void json_dump_recursive(grpc_json_writer *writer, grpc_json *json, + int in_object) { + while (json) { + if (in_object) grpc_json_writer_object_key(writer, json->key); + + switch (json->type) { + case GRPC_JSON_OBJECT: + case GRPC_JSON_ARRAY: + grpc_json_writer_container_begins(writer, json->type); + if (json->child) + json_dump_recursive(writer, json->child, + json->type == GRPC_JSON_OBJECT); + grpc_json_writer_container_ends(writer, json->type); + break; + case GRPC_JSON_STRING: + grpc_json_writer_value_string(writer, json->value); + break; + case GRPC_JSON_NUMBER: + grpc_json_writer_value_raw(writer, json->value); + break; + case GRPC_JSON_TRUE: + grpc_json_writer_value_raw_with_len(writer, "true", 4); + break; + case GRPC_JSON_FALSE: + grpc_json_writer_value_raw_with_len(writer, "false", 5); + break; + case GRPC_JSON_NULL: + grpc_json_writer_value_raw_with_len(writer, "null", 4); + break; + default: + GPR_UNREACHABLE_CODE(abort()); + } + json = json->next; + } +} + +static grpc_json_writer_vtable writer_vtable = { + json_writer_output_char, json_writer_output_string, + json_writer_output_string_with_len}; + +char *grpc_json_dump_to_string(grpc_json *json, int indent) { + grpc_json_writer writer; + json_writer_userdata state; + + state.output = NULL; + state.free_space = state.string_len = state.allocated = 0; + grpc_json_writer_init(&writer, indent, &writer_vtable, &state); + + json_dump_recursive(&writer, json, 0); + + json_writer_output_char(&state, 0); + + return state.output; +} diff --git a/src/core/lib/json/json_writer.c b/src/core/lib/json/json_writer.c new file mode 100644 index 0000000000..326ec2d431 --- /dev/null +++ b/src/core/lib/json/json_writer.c @@ -0,0 +1,258 @@ +/* + * + * Copyright 2015, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include + +#include + +#include "src/core/json/json_writer.h" + +static void json_writer_output_char(grpc_json_writer *writer, char c) { + writer->vtable->output_char(writer->userdata, c); +} + +static void json_writer_output_string(grpc_json_writer *writer, + const char *str) { + writer->vtable->output_string(writer->userdata, str); +} + +static void json_writer_output_string_with_len(grpc_json_writer *writer, + const char *str, size_t len) { + writer->vtable->output_string_with_len(writer->userdata, str, len); +} + +void grpc_json_writer_init(grpc_json_writer *writer, int indent, + grpc_json_writer_vtable *vtable, void *userdata) { + memset(writer, 0, sizeof(*writer)); + writer->container_empty = 1; + writer->indent = indent; + writer->vtable = vtable; + writer->userdata = userdata; +} + +static void json_writer_output_indent(grpc_json_writer *writer) { + static const char spacesstr[] = + " " + " " + " " + " "; + + unsigned spaces = (unsigned)(writer->depth * writer->indent); + + if (writer->indent == 0) return; + + if (writer->got_key) { + json_writer_output_char(writer, ' '); + return; + } + + while (spaces >= (sizeof(spacesstr) - 1)) { + json_writer_output_string_with_len(writer, spacesstr, + sizeof(spacesstr) - 1); + spaces -= (unsigned)(sizeof(spacesstr) - 1); + } + + if (spaces == 0) return; + + json_writer_output_string_with_len( + writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); +} + +static void json_writer_value_end(grpc_json_writer *writer) { + if (writer->container_empty) { + writer->container_empty = 0; + if ((writer->indent == 0) || (writer->depth == 0)) return; + json_writer_output_char(writer, '\n'); + } else { + json_writer_output_char(writer, ','); + if (writer->indent == 0) return; + json_writer_output_char(writer, '\n'); + } +} + +static void json_writer_escape_utf16(grpc_json_writer *writer, uint16_t utf16) { + static const char hex[] = "0123456789abcdef"; + + json_writer_output_string_with_len(writer, "\\u", 2); + json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]); + json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]); + json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]); + json_writer_output_char(writer, hex[(utf16)&0x0f]); +} + +static void json_writer_escape_string(grpc_json_writer *writer, + const char *string) { + json_writer_output_char(writer, '"'); + + for (;;) { + uint8_t c = (uint8_t)*string++; + if (c == 0) { + break; + } else if ((c >= 32) && (c <= 126)) { + if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\'); + json_writer_output_char(writer, (char)c); + } else if ((c < 32) || (c == 127)) { + switch (c) { + case '\b': + json_writer_output_string_with_len(writer, "\\b", 2); + break; + case '\f': + json_writer_output_string_with_len(writer, "\\f", 2); + break; + case '\n': + json_writer_output_string_with_len(writer, "\\n", 2); + break; + case '\r': + json_writer_output_string_with_len(writer, "\\r", 2); + break; + case '\t': + json_writer_output_string_with_len(writer, "\\t", 2); + break; + default: + json_writer_escape_utf16(writer, c); + break; + } + } else { + uint32_t utf32 = 0; + int extra = 0; + int i; + int valid = 1; + if ((c & 0xe0) == 0xc0) { + utf32 = c & 0x1f; + extra = 1; + } else if ((c & 0xf0) == 0xe0) { + utf32 = c & 0x0f; + extra = 2; + } else if ((c & 0xf8) == 0xf0) { + utf32 = c & 0x07; + extra = 3; + } else { + break; + } + for (i = 0; i < extra; i++) { + utf32 <<= 6; + c = (uint8_t)(*string++); + /* Breaks out and bail on any invalid UTF-8 sequence, including \0. */ + if ((c & 0xc0) != 0x80) { + valid = 0; + break; + } + utf32 |= c & 0x3f; + } + if (!valid) break; + /* The range 0xd800 - 0xdfff is reserved by the surrogates ad vitam. + * Any other range is technically reserved for future usage, so if we + * don't want the software to break in the future, we have to allow + * anything else. The first non-unicode character is 0x110000. */ + if (((utf32 >= 0xd800) && (utf32 <= 0xdfff)) || (utf32 >= 0x110000)) + break; + if (utf32 >= 0x10000) { + /* If utf32 contains a character that is above 0xffff, it needs to be + * broken down into a utf-16 surrogate pair. A surrogate pair is first + * a high surrogate, followed by a low surrogate. Each surrogate holds + * 10 bits of usable data, thus allowing a total of 20 bits of data. + * The high surrogate marker is 0xd800, while the low surrogate marker + * is 0xdc00. The low 10 bits of each will be the usable data. + * + * After re-combining the 20 bits of data, one has to add 0x10000 to + * the resulting value, in order to obtain the original character. + * This is obviously because the range 0x0000 - 0xffff can be written + * without any special trick. + * + * Since 0x10ffff is the highest allowed character, we're working in + * the range 0x00000 - 0xfffff after we decrement it by 0x10000. + * That range is exactly 20 bits. + */ + utf32 -= 0x10000; + json_writer_escape_utf16(writer, (uint16_t)(0xd800 | (utf32 >> 10))); + json_writer_escape_utf16(writer, (uint16_t)(0xdc00 | (utf32 & 0x3ff))); + } else { + json_writer_escape_utf16(writer, (uint16_t)utf32); + } + } + } + + json_writer_output_char(writer, '"'); +} + +void grpc_json_writer_container_begins(grpc_json_writer *writer, + grpc_json_type type) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); + writer->container_empty = 1; + writer->got_key = 0; + writer->depth++; +} + +void grpc_json_writer_container_ends(grpc_json_writer *writer, + grpc_json_type type) { + if (writer->indent && !writer->container_empty) + json_writer_output_char(writer, '\n'); + writer->depth--; + if (!writer->container_empty) json_writer_output_indent(writer); + json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']'); + writer->container_empty = 0; + writer->got_key = 0; +} + +void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string) { + json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_escape_string(writer, string); + json_writer_output_char(writer, ':'); + writer->got_key = 1; +} + +void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_string(writer, string); + writer->got_key = 0; +} + +void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, + const char *string, size_t len) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_output_string_with_len(writer, string, len); + writer->got_key = 0; +} + +void grpc_json_writer_value_string(grpc_json_writer *writer, + const char *string) { + if (!writer->got_key) json_writer_value_end(writer); + json_writer_output_indent(writer); + json_writer_escape_string(writer, string); + writer->got_key = 0; +} diff --git a/src/core/lib/json/json_writer.h b/src/core/lib/json/json_writer.h new file mode 100644 index 0000000000..c392126950 --- /dev/null +++ b/src/core/lib/json/json_writer.h @@ -0,0 +1,97 @@ +/* + * + * Copyright 2015-2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* The idea of the writer is basically symmetrical of the reader. While the + * reader emits various calls to your code, the writer takes basically the + * same calls and emit json out of it. It doesn't try to make any check on + * the order of the calls you do on it. Meaning you can theorically force + * it to generate invalid json. + * + * Also, unlike the reader, the writer expects UTF-8 encoded input strings. + * These strings will be UTF-8 validated, and any invalid character will + * cut the conversion short, before any invalid UTF-8 sequence, thus forming + * a valid UTF-8 string overall. + */ + +#ifndef GRPC_CORE_JSON_JSON_WRITER_H +#define GRPC_CORE_JSON_JSON_WRITER_H + +#include + +#include "src/core/json/json_common.h" + +typedef struct grpc_json_writer_vtable { + /* Adds a character to the output stream. */ + void (*output_char)(void *userdata, char); + /* Adds a zero-terminated string to the output stream. */ + void (*output_string)(void *userdata, const char *str); + /* Adds a fixed-length string to the output stream. */ + void (*output_string_with_len)(void *userdata, const char *str, size_t len); + +} grpc_json_writer_vtable; + +typedef struct grpc_json_writer { + void *userdata; + grpc_json_writer_vtable *vtable; + int indent; + int depth; + int container_empty; + int got_key; +} grpc_json_writer; + +/* Call this to initialize your writer structure. The indent parameter is + * specifying the number of spaces to use for indenting the output. If you + * use indent=0, then the output will not have any newlines either, thus + * emitting a condensed json output. + */ +void grpc_json_writer_init(grpc_json_writer *writer, int indent, + grpc_json_writer_vtable *vtable, void *userdata); + +/* Signals the beginning of a container. */ +void grpc_json_writer_container_begins(grpc_json_writer *writer, + grpc_json_type type); +/* Signals the end of a container. */ +void grpc_json_writer_container_ends(grpc_json_writer *writer, + grpc_json_type type); +/* Writes down an object key for the next value. */ +void grpc_json_writer_object_key(grpc_json_writer *writer, const char *string); +/* Sets a raw value. Useful for numbers. */ +void grpc_json_writer_value_raw(grpc_json_writer *writer, const char *string); +/* Sets a raw value with its length. Useful for values like true or false. */ +void grpc_json_writer_value_raw_with_len(grpc_json_writer *writer, + const char *string, size_t len); +/* Sets a string value. It'll be escaped, and utf-8 validated. */ +void grpc_json_writer_value_string(grpc_json_writer *writer, + const char *string); + +#endif /* GRPC_CORE_JSON_JSON_WRITER_H */ -- cgit v1.2.3