diff options
author | 2015-01-26 17:01:29 -0800 | |
---|---|---|
committer | 2015-01-26 17:02:45 -0800 | |
commit | e04455a7ff1ead05445daa95d7bc822a7f40b6f5 (patch) | |
tree | f3d7d23b6f89387d80700b1900de4453531313e1 /src/core/json | |
parent | fee065c1c7f01207c0e484c92681cea184b1983a (diff) |
Addressing comments.
Diffstat (limited to 'src/core/json')
-rw-r--r-- | src/core/json/json-reader-defs.h | 95 | ||||
-rw-r--r-- | src/core/json/json-writer-defs.h | 46 | ||||
-rw-r--r-- | src/core/json/json.c | 4 | ||||
-rw-r--r-- | src/core/json/json.h | 22 | ||||
-rw-r--r-- | src/core/json/json_common.h (renamed from src/core/json/json-defs.h) | 13 | ||||
-rw-r--r-- | src/core/json/json_reader.c (renamed from src/core/json/json-reader-impl.h) | 208 | ||||
-rw-r--r-- | src/core/json/json_reader.h | 158 | ||||
-rw-r--r-- | src/core/json/json_string.c (renamed from src/core/json/json-string.c) | 283 | ||||
-rw-r--r-- | src/core/json/json_writer.c (renamed from src/core/json/json-writer-impl.h) | 155 | ||||
-rw-r--r-- | src/core/json/json_writer.h | 92 |
10 files changed, 619 insertions, 457 deletions
diff --git a/src/core/json/json-reader-defs.h b/src/core/json/json-reader-defs.h deleted file mode 100644 index 10c84738b3..0000000000 --- a/src/core/json/json-reader-defs.h +++ /dev/null @@ -1,95 +0,0 @@ -/* - * - * Copyright 2014, 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 following need to be pre-defined: - * grpc_json_reader_opaque_t // A type you can use to keep track of your - * // own stuff. - * grpc_json_wchar_t // A type that can hold a unicode character - * // unsigned is good enough. - * grpc_json_string_t // A type that can hold a growable string. - */ - -enum grpc_json_reader_state_t { - 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 -}; - -struct grpc_json_reader_t { - /* You are responsible for the initialization of the following. */ - grpc_json_reader_opaque_t opaque; - - /* Everything down here is private, - and initialized by grpc_json_reader_init. */ - int depth; - int in_object; - int in_array; - int escaped_string_was_key; - int container_just_begun; - grpc_json_wchar_t unicode; - enum grpc_json_reader_state_t state; -}; - -/* 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_ret_t; diff --git a/src/core/json/json-writer-defs.h b/src/core/json/json-writer-defs.h deleted file mode 100644 index f8ab9885a2..0000000000 --- a/src/core/json/json-writer-defs.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * - * Copyright 2014, 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 following need to be pre-defined: - * grpc_json_writer_opaque_t // A type you can use to keep track of your - * // own stuff. - */ - -struct grpc_json_writer_t { - grpc_json_writer_opaque_t opaque; - - int indent; - int depth; - int container_empty; - int got_key; -}; diff --git a/src/core/json/json.c b/src/core/json/json.c index 2905abadcf..e7fcec31ff 100644 --- a/src/core/json/json.c +++ b/src/core/json/json.c @@ -35,8 +35,8 @@ #include "src/core/json/json.h" -grpc_json *grpc_json_new(enum grpc_json_type_t type) { - grpc_json *json = (grpc_json *)gpr_malloc(sizeof(grpc_json)); +grpc_json *grpc_json_new(grpc_json_type type) { + grpc_json *json = gpr_malloc(sizeof(grpc_json)); json->parent = json->child = json->next = json->prev = NULL; json->type = type; diff --git a/src/core/json/json.h b/src/core/json/json.h index a0bc4dfffc..433ba22586 100644 --- a/src/core/json/json.h +++ b/src/core/json/json.h @@ -36,12 +36,18 @@ #include <stdlib.h> -#include "src/core/json/json-defs.h" +#include "src/core/json/json_common.h" -typedef struct grpc_json_t { - struct grpc_json_t* next, *prev, *child, *parent; - enum grpc_json_type_t type; +/* 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; @@ -51,7 +57,11 @@ typedef struct grpc_json_t { * 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. + * 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_t objects will be strings + * pointing at your input buffer. * * Delete the allocated tree afterward using grpc_json_delete(). */ @@ -72,7 +82,7 @@ char* grpc_json_dump_to_string(grpc_json* json, int indent); * 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_new(enum grpc_json_type_t type); +grpc_json* grpc_json_new(grpc_json_type type); void grpc_json_delete(grpc_json* json); #endif /* __GRPC_SRC_CORE_JSON_JSON_H__ */ diff --git a/src/core/json/json-defs.h b/src/core/json/json_common.h index decf35e4a2..88a8155a42 100644 --- a/src/core/json/json-defs.h +++ b/src/core/json/json_common.h @@ -31,8 +31,11 @@ * */ -/* The various json types. "NONE" may only used on top-level. */ -enum grpc_json_type_t { +#ifndef __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ +#define __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ + +/* The various json types. */ +typedef enum { GRPC_JSON_OBJECT, GRPC_JSON_ARRAY, GRPC_JSON_STRING, @@ -40,5 +43,7 @@ enum grpc_json_type_t { GRPC_JSON_TRUE, GRPC_JSON_FALSE, GRPC_JSON_NULL, - GRPC_JSON_NONE -}; + GRPC_JSON_TOP_LEVEL +} grpc_json_type; + +#endif /* __GRPC_SRC_CORE_JSON_JSON_COMMON_H__ */ diff --git a/src/core/json/json-reader-impl.h b/src/core/json/json_reader.c index 2fe56959d0..c3877966dd 100644 --- a/src/core/json/json-reader-impl.h +++ b/src/core/json/json_reader.c @@ -31,113 +31,93 @@ * */ -/* the following need to be pre-defined: - * grpc_json_static_inline // A macro to declare a static inline - * // function - * grpc_json_eof // A macro that can be used in a switch - * // statement, that grpc_json_read_char - * // can return - * grpc_json_eagain // A macro that can be used in a switch - * // statement, that grpc_json_read_char - * // can return - * grpc_json_error // A macro that can be used in a switch - * // statement, that grpc_json_read_char - * // can return - * - * // A macro or a function that clears your internal scratchpad. - * grpc_json_reader_string_clear(struct grpc_json_reader_t*); - * // A macro or a function that adds a character to your internal - * // scratchpad. - * grpc_json_reader_string_add_char(struct grpc_json_reader_t*, int); - * // A macro or a function that adds a unicode character to your internal - * // scratchpad. - * grpc_json_reader_string_add_wchar(struct grpc_json_reader_t*, - * grpc_json_wchar_t); - * - * // A macro or a function that returns the next character from the input. - * // It can return: - * // . an actual character into an int - unicode, wchar_t, whatever, as - * // long as it's going to work in a switch statement, and can be tested - * // against typical json tokens, such as '{', '[', ',', '}', ']', digits - * // and whitespaces. - * // . grpc_json_eof, which means the end of the input has been reached. - * // . grpc_json_eagain, which means the parser needs to yield before - * // getting more input. - * // . grpc_json_error, which means the parser needs to exit with an error. - * int grpc_json_reader_read_char(struct grpc_json_reader_t*); - * - * // A macro or a function that will be called to signal the beginning of a - * // container. - * // The argument "type" can be either GRPC_JSON_OBJECT, or GRPC_JSON_ARRAY. - * void grpc_json_reader_container_begins(struct grpc_json_reader_t*, - * enum *grpc_json_type_t type) - * // A macro or a function that will be called to signal the end of the - * // current container. It must return GRPC_JSON_OBJECT or GRPC_JSON_ARRAY - * // to signal what is the new current container, or GRPC_JSON_NONE if the - * // stack of containers is now empty. - * enum grpc_json_type_t - * grpc_json_reader_container_ends(struct grpc_json_reader_t*); - * - * // A macro or a function that will be called to signal that json->string - * // contains the string of a object's key that is being added. - * void grpc_json_reader_object_set_key(struct grpc_json_reader_t*); - * - * // A set of macro or functions that will be called to signal that the - * // current container is getting a new value. set_string and set_number - * // are reading their value from your internal scratchpad. set_number - * // must return a boolean to signal if the number parsing succeeded or - * // not. There is little reason for it not to. - * void grpc_json_reader_container_set_string(struct grpc_json_reader_t*); - * int grpc_json_reader_container_set_number(struct grpc_json_reader_t*); - * void grpc_json_reader_container_set_true(struct grpc_json_reader_t*); - * void grpc_json_reader_container_set_false(struct grpc_json_reader_t*); - * void grpc_json_reader_container_set_null(struct grpc_json_reader_t*); - */ +#include <grpc/support/port_platform.h> +#include "src/core/json/json_reader.h" + +static void grpc_json_reader_string_clear(grpc_json_reader* reader) { + reader->string_clear(reader); +} + +static void grpc_json_reader_string_add_char(grpc_json_reader* reader, + gpr_uint32 c) { + reader->string_add_char(reader, c); +} + +static void grpc_json_reader_string_add_utf32(grpc_json_reader* reader, + gpr_uint32 utf32) { + reader->string_add_utf32(reader, utf32); +} + +static gpr_uint32 + grpc_json_reader_read_char(grpc_json_reader* reader) { + return reader->read_char(reader); +} + +static void grpc_json_reader_container_begins(grpc_json_reader* reader, + grpc_json_type type) { + reader->container_begins(reader, type); +} + +static grpc_json_type + grpc_json_reader_container_ends(grpc_json_reader* reader) { + return reader->container_ends(reader); +} + +static void grpc_json_reader_set_key(grpc_json_reader* reader) { + reader->set_key(reader); +} + +static void grpc_json_reader_set_string(grpc_json_reader* reader) { + reader->set_string(reader); +} + +static int grpc_json_reader_set_number(grpc_json_reader* reader) { + return reader->set_number(reader); +} + +static void grpc_json_reader_set_true(grpc_json_reader* reader) { + reader->set_true(reader); +} + +static void grpc_json_reader_set_false(grpc_json_reader* reader) { + reader->set_false(reader); +} + +static void grpc_json_reader_set_null(grpc_json_reader* reader) { + reader->set_null(reader); +} /* Call this function to initialize the reader structure. */ -grpc_json_static_inline void grpc_json_reader_init( - struct grpc_json_reader_t* reader) { +void grpc_json_reader_init(grpc_json_reader* reader) { reader->depth = 0; reader->in_object = 0; reader->in_array = 0; + reader->unicode_high_surrogate = 0; grpc_json_reader_string_clear(reader); reader->state = GRPC_JSON_STATE_VALUE_BEGIN; } -/* 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. - */ +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_static_inline grpc_json_reader_ret_t -grpc_json_reader_run(struct grpc_json_reader_t* reader) { +grpc_json_reader_ret grpc_json_reader_run(grpc_json_reader* reader) { int c, success; - /* This state-machine is a strict implementation of http://json.org/ */ + /* 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_error: + case GRPC_JSON_READ_CHAR_ERROR: return GRPC_JSON_READ_ERROR; - case grpc_json_eagain: + case GRPC_JSON_READ_CHAR_EAGAIN: return GRPC_JSON_EAGAIN; - case grpc_json_eof: - if ((reader->depth == 0) && - ((reader->state == GRPC_JSON_STATE_END) || - (reader->state == GRPC_JSON_STATE_VALUE_END))) { + case GRPC_JSON_READ_CHAR_EOF: + if (grpc_json_reader_is_complete(reader)) { return GRPC_JSON_DONE; } else { return GRPC_JSON_PARSE_ERROR; @@ -159,6 +139,8 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { 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) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_add_char(reader, c); break; @@ -166,7 +148,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - success = grpc_json_reader_container_set_number(reader); + success = grpc_json_reader_set_number(reader); if (!success) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_clear(reader); reader->state = GRPC_JSON_STATE_VALUE_END; @@ -184,6 +166,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { switch (reader->state) { case GRPC_JSON_STATE_OBJECT_KEY_STRING: case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_add_char(reader, c); break; @@ -191,7 +174,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { case GRPC_JSON_STATE_VALUE_NUMBER_WITH_DECIMAL: case GRPC_JSON_STATE_VALUE_NUMBER_ZERO: case GRPC_JSON_STATE_VALUE_NUMBER_EPM: - success = grpc_json_reader_container_set_number(reader); + success = grpc_json_reader_set_number(reader); if (!success) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_clear(reader); reader->state = GRPC_JSON_STATE_VALUE_END; @@ -235,7 +218,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { reader->in_object = 0; reader->in_array = 1; break; - case GRPC_JSON_NONE: + case GRPC_JSON_TOP_LEVEL: if (reader->depth != 0) return GRPC_JSON_INTERNAL_ERROR; reader->in_object = 0; reader->in_array = 0; @@ -267,6 +250,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { /* This is the \\ case. */ case GRPC_JSON_STATE_STRING_ESCAPE: + if (reader->unicode_high_surrogate) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_add_char(reader, '\\'); if (reader->escaped_string_was_key) { reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; @@ -289,21 +273,25 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { break; case GRPC_JSON_STATE_OBJECT_KEY_STRING: + if (reader->unicode_high_surrogate) return GRPC_JSON_PARSE_ERROR; if (c == '"') { reader->state = GRPC_JSON_STATE_OBJECT_KEY_END; - grpc_json_reader_object_set_key(reader); + grpc_json_reader_set_key(reader); grpc_json_reader_string_clear(reader); } else { + if (c < 32) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_add_char(reader, c); } break; case GRPC_JSON_STATE_VALUE_STRING: + if (reader->unicode_high_surrogate) return GRPC_JSON_PARSE_ERROR; if (c == '"') { reader->state = GRPC_JSON_STATE_VALUE_END; - grpc_json_reader_container_set_string(reader); + grpc_json_reader_set_string(reader); grpc_json_reader_string_clear(reader); } else { + if (c < 32) return GRPC_JSON_PARSE_ERROR; grpc_json_reader_string_add_char(reader, c); } break; @@ -374,6 +362,8 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { } else { reader->state = GRPC_JSON_STATE_VALUE_STRING; } + if (reader->unicode_high_surrogate && c != 'u') + return GRPC_JSON_PARSE_ERROR; switch (c) { case '"': case '/': @@ -396,7 +386,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { break; case 'u': reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U1; - reader->unicode = 0; + reader->unicode_char = 0; break; default: return GRPC_JSON_PARSE_ERROR; @@ -416,8 +406,8 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { } else { return GRPC_JSON_PARSE_ERROR; } - reader->unicode <<= 4; - reader->unicode |= c; + reader->unicode_char <<= 4; + reader->unicode_char |= c; switch (reader->state) { case GRPC_JSON_STATE_STRING_ESCAPE_U1: @@ -430,7 +420,27 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { reader->state = GRPC_JSON_STATE_STRING_ESCAPE_U4; break; case GRPC_JSON_STATE_STRING_ESCAPE_U4: - grpc_json_reader_string_add_wchar(reader, reader->unicode); + if ((reader->unicode_char & 0xfc00) == 0xd800) { + /* high surrogate utf-16 */ + if (reader->unicode_high_surrogate) + return GRPC_JSON_PARSE_ERROR; + reader->unicode_high_surrogate = reader->unicode_char; + } else if ((reader->unicode_char & 0xfc00) == 0xdc00) { + /* low surrogate utf-16 */ + gpr_uint32 utf32; + if (!reader->unicode_high_surrogate) + return GRPC_JSON_PARSE_ERROR; + utf32 = 0x10000; + utf32 += (reader->unicode_high_surrogate - 0xd800) * 0x400; + utf32 += reader->unicode_char - 0xdc00; + grpc_json_reader_string_add_utf32(reader, utf32); + reader->unicode_high_surrogate = 0; + } else { + /* anything else */ + if (reader->unicode_high_surrogate) + return GRPC_JSON_PARSE_ERROR; + grpc_json_reader_string_add_utf32(reader, reader->unicode_char); + } if (reader->escaped_string_was_key) { reader->state = GRPC_JSON_STATE_OBJECT_KEY_STRING; } else { @@ -564,7 +574,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { case GRPC_JSON_STATE_VALUE_TRUE_E: if (c != 'e') return GRPC_JSON_PARSE_ERROR; - grpc_json_reader_container_set_true(reader); + grpc_json_reader_set_true(reader); reader->state = GRPC_JSON_STATE_VALUE_END; break; @@ -585,7 +595,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { case GRPC_JSON_STATE_VALUE_FALSE_E: if (c != 'e') return GRPC_JSON_PARSE_ERROR; - grpc_json_reader_container_set_false(reader); + grpc_json_reader_set_false(reader); reader->state = GRPC_JSON_STATE_VALUE_END; break; @@ -601,7 +611,7 @@ grpc_json_reader_run(struct grpc_json_reader_t* reader) { case GRPC_JSON_STATE_VALUE_NULL_L2: if (c != 'l') return GRPC_JSON_PARSE_ERROR; - grpc_json_reader_container_set_null(reader); + grpc_json_reader_set_null(reader); reader->state = GRPC_JSON_STATE_VALUE_END; break; diff --git a/src/core/json/json_reader.h b/src/core/json/json_reader.h new file mode 100644 index 0000000000..d71f0b8e66 --- /dev/null +++ b/src/core/json/json_reader.h @@ -0,0 +1,158 @@ +/* + * + * Copyright 2014, 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_SRC_CORE_JSON_JSON_READER_H__ +#define __GRPC_SRC_CORE_JSON_JSON_READER_H__ + +#include <grpc/support/port_platform.h> +#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 +}; + +typedef struct grpc_json_reader { + /* You are responsible for your own opaque userdata. + * Among other things, it needs to hold a string scratchpad. + */ + void* userdata; + + /* You also need to set up these callbacks. */ + + /* Clears your internal string scratchpad. */ + void (*string_clear)(struct grpc_json_reader*); + /* Adds a char to the string scratchpad. */ + void (*string_add_char)(struct grpc_json_reader*, gpr_uint32 c); + /* Adds a utf32 char to the string scratchpad. */ + void (*string_add_utf32)(struct grpc_json_reader*, gpr_uint32 c); + /* Reads a character from your input. May be utf-8, 16 or 32. */ + gpr_uint32 (*read_char)(struct grpc_json_reader*); + /* Starts a container of type GRPC_JSON_ARRAY or GRPC_JSON_OBJECT. */ + void (*container_begins)(struct grpc_json_reader*, grpc_json_type type); + /* Ends the current container. Must return the type of its parent. */ + grpc_json_type (*container_ends)(struct grpc_json_reader*); + /* Your internal string scratchpad is an object's key. */ + void (*set_key)(struct grpc_json_reader*); + /* Your internal string scratchpad is a string value. */ + void (*set_string)(struct grpc_json_reader*); + /* Your internal string scratchpad is a numerical value. Return 1 if valid. */ + int (*set_number)(struct grpc_json_reader*); + /* Sets the values true, false or null. */ + void (*set_true)(struct grpc_json_reader*); + void (*set_false)(struct grpc_json_reader*); + void (*set_null)(struct grpc_json_reader*); + + /* Everything down here is private, + and initialized by grpc_json_reader_init. */ + int depth; + int in_object; + int in_array; + int escaped_string_was_key; + int container_just_begun; + gpr_uint16 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_ret; + +/* 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_ret 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); + +/* 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_SRC_CORE_JSON_JSON_READER_H__ */ diff --git a/src/core/json/json-string.c b/src/core/json/json_string.c index 771c2cc85e..12309c4dbc 100644 --- a/src/core/json/json-string.c +++ b/src/core/json/json_string.c @@ -32,23 +32,16 @@ */ #include <string.h> +#include <stdlib.h> #include <grpc/support/alloc.h> #include <grpc/support/log.h> #include "src/core/json/json.h" +#include "src/core/json/json_reader.h" +#include "src/core/json/json_writer.h" -/* 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, string_len, allocated; -} grpc_json_writer_opaque_t; - -#include "src/core/json/json-writer-defs.h" - -/* The json-reader will construct a bunch of grpc_json objects and +/* 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. * @@ -60,88 +53,86 @@ typedef struct { * input size, and never expands it. */ typedef struct { - grpc_json *top; - grpc_json *current_container; - grpc_json *current_value; - char *input; - char *key; - char *string; - char *string_ptr; + grpc_json* top; + grpc_json* current_container; + grpc_json* current_value; + char* input; + char* key; + char* string; + char* string_ptr; size_t remaining_input; -} grpc_json_reader_opaque_t; - -typedef unsigned grpc_json_wchar_t; +} grpc_json_reader_opaque; -#include "src/core/json/json-reader-defs.h" +/* 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, string_len, allocated; +} grpc_json_writer_opaque; -/* Next up, the definitions needed for the implementation. */ -#define grpc_json_static_inline static -#define grpc_json_eof -1 -#define grpc_json_eagain -2 -#define grpc_json_error -3 -/* This functions checks if there's enough space left in the output buffer, +/* 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 grpc_json_writer_output_check(struct grpc_json_writer_t *writer, +static void grpc_json_writer_output_check(grpc_json_writer* writer, size_t needed) { - if (writer->opaque.free_space >= needed) return; - needed = (needed - writer->opaque.free_space + 0xff) & ~0xff; - writer->opaque.output = (char *)gpr_realloc( - writer->opaque.output, writer->opaque.allocated + needed); - writer->opaque.free_space += needed; - writer->opaque.allocated += needed; + grpc_json_writer_opaque* state = writer->userdata; + if (state->free_space >= needed) return; + needed -= state->free_space; + /* Round up by 256 bytes. */ + needed = (needed + 0xff) & ~0xff; + 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 grpc_json_writer_output_char(struct grpc_json_writer_t *writer, +static void grpc_json_writer_output_char(grpc_json_writer* writer, char c) { + grpc_json_writer_opaque* state = writer->userdata; grpc_json_writer_output_check(writer, 1); - writer->opaque.output[writer->opaque.string_len++] = c; - writer->opaque.free_space--; + state->output[state->string_len++] = c; + state->free_space--; } static void grpc_json_writer_output_string_with_len( - struct grpc_json_writer_t *writer, const char *str, size_t len) { + grpc_json_writer* writer, const char* str, size_t len) { + grpc_json_writer_opaque* state = writer->userdata; grpc_json_writer_output_check(writer, len); - memcpy(writer->opaque.output + writer->opaque.string_len, str, len); - writer->opaque.string_len += len; - writer->opaque.free_space -= len; + memcpy(state->output + state->string_len, str, len); + state->string_len += len; + state->free_space -= len; } -static void grpc_json_writer_output_string(struct grpc_json_writer_t *writer, - const char *str) { +static void grpc_json_writer_output_string(grpc_json_writer* writer, + const char* str) { size_t len = strlen(str); grpc_json_writer_output_string_with_len(writer, str, len); } -#include "src/core/json/json-writer-impl.h" - /* 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 grpc_json_reader_string_clear(struct grpc_json_reader_t *reader) { - if (reader->opaque.string) { - GPR_ASSERT(reader->opaque.string_ptr < reader->opaque.input); - *reader->opaque.string_ptr++ = 0; +static void grpc_json_reader_string_clear(grpc_json_reader* reader) { + grpc_json_reader_opaque* state = reader->userdata; + if (state->string) { + GPR_ASSERT(state->string_ptr < state->input); + *state->string_ptr++ = 0; } - reader->opaque.string = reader->opaque.string_ptr; + state->string = state->string_ptr; } -static void grpc_json_reader_string_add_char(struct grpc_json_reader_t *reader, - int c) { - GPR_ASSERT(reader->opaque.string_ptr < reader->opaque.input); - *reader->opaque.string_ptr++ = (char)c; +static void grpc_json_reader_string_add_char(grpc_json_reader* reader, gpr_uint32 c) { + grpc_json_reader_opaque* state = reader->userdata; + GPR_ASSERT(state->string_ptr < state->input); + GPR_ASSERT(c <= 0xff); + *state->string_ptr++ = (char)c; } -/* We are converting a unicode character into utf-8 here. */ -/* The unicode escape encoding of json can only hold 16-bits values. - * So the the 4th case, as well as the last test aren't techically - * necessary, but I wrote them anyway for completion. - */ -static void grpc_json_reader_string_add_wchar(struct grpc_json_reader_t *reader, - unsigned int c) { +/* We are converting a UTF-32 character into UTF-8 here. */ +static void grpc_json_reader_string_add_utf32(grpc_json_reader* reader, gpr_uint32 c) { if (c <= 0x7f) { grpc_json_reader_string_add_char(reader, c); } else if (c <= 0x7ff) { @@ -171,19 +162,18 @@ static void grpc_json_reader_string_add_wchar(struct grpc_json_reader_t *reader, /* 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 int grpc_json_reader_read_char(struct grpc_json_reader_t *reader) { - int r; +static gpr_uint32 grpc_json_reader_read_char(grpc_json_reader* reader) { + gpr_uint32 r; + grpc_json_reader_opaque* state = reader->userdata; - if (reader->opaque.remaining_input == 0) { - return grpc_json_eof; - } + if (state->remaining_input == 0) return GRPC_JSON_READ_CHAR_EOF; - r = *reader->opaque.input++; - reader->opaque.remaining_input--; + r = *state->input++; + state->remaining_input--; if (r == 0) { - reader->opaque.remaining_input = 0; - return grpc_json_eof; + state->remaining_input = 0; + return GRPC_JSON_READ_CHAR_EOF; } return r; @@ -192,13 +182,14 @@ static int grpc_json_reader_read_char(struct grpc_json_reader_t *reader) { /* Helper function to create a new grpc_json object and link it into * our tree-in-progress inside our opaque structure. */ -static grpc_json *grpc_json_new_and_link(struct grpc_json_reader_t *reader, - enum grpc_json_type_t type) { - grpc_json *json = grpc_json_new(type); +static grpc_json* grpc_json_new_and_link(grpc_json_reader* reader, + grpc_json_type type) { + grpc_json_reader_opaque* state = reader->userdata; + grpc_json* json = grpc_json_new(type); - json->parent = reader->opaque.current_container; - json->prev = reader->opaque.current_value; - reader->opaque.current_value = json; + json->parent = state->current_container; + json->prev = state->current_value; + state->current_value = json; if (json->prev) { json->prev->next = json; @@ -208,47 +199,49 @@ static grpc_json *grpc_json_new_and_link(struct grpc_json_reader_t *reader, json->parent->child = json; } if (json->parent->type == GRPC_JSON_OBJECT) { - json->key = reader->opaque.key; + json->key = state->key; } } - if (!reader->opaque.top) { - reader->opaque.top = json; + if (!state->top) { + state->top = json; } return json; } -static void grpc_json_reader_container_begins(struct grpc_json_reader_t *reader, - enum grpc_json_type_t type) { - grpc_json *container; +static void grpc_json_reader_container_begins(grpc_json_reader* reader, + grpc_json_type type) { + grpc_json_reader_opaque* state = reader->userdata; + grpc_json* container; GPR_ASSERT(type == GRPC_JSON_ARRAY || type == GRPC_JSON_OBJECT); container = grpc_json_new_and_link(reader, type); - reader->opaque.current_container = container; - reader->opaque.current_value = NULL; + state->current_container = container; + state->current_value = NULL; } -/* It's important to remember that the reader is mostly state-less, so it - * isn't trying to remember what was the container prior the one that just +/* 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_NONE. + * ends, we have to return GRPC_JSON_TOP_LEVEL. */ -static enum grpc_json_type_t grpc_json_reader_container_ends( - struct grpc_json_reader_t *reader) { - enum grpc_json_type_t container_type = GRPC_JSON_NONE; +static grpc_json_type grpc_json_reader_container_ends( + grpc_json_reader* reader) { + grpc_json_type container_type = GRPC_JSON_TOP_LEVEL; + grpc_json_reader_opaque* state = reader->userdata; - GPR_ASSERT(reader->opaque.current_container); + GPR_ASSERT(state->current_container); - reader->opaque.current_value = reader->opaque.current_container; - reader->opaque.current_container = reader->opaque.current_container->parent; + state->current_value = state->current_container; + state->current_container = state->current_container->parent; - if (reader->opaque.current_container) { - container_type = reader->opaque.current_container->type; + if (state->current_container) { + container_type = state->current_container->type; } return container_type; @@ -260,62 +253,74 @@ static enum grpc_json_type_t grpc_json_reader_container_ends( * 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 grpc_json_reader_object_set_key(struct grpc_json_reader_t *reader) { - reader->opaque.key = reader->opaque.string; +static void grpc_json_reader_set_key(grpc_json_reader* reader) { + grpc_json_reader_opaque* state = reader->userdata; + state->key = state->string; } -static void grpc_json_reader_container_set_string( - struct grpc_json_reader_t *reader) { - grpc_json *json = grpc_json_new_and_link(reader, GRPC_JSON_STRING); - json->value = reader->opaque.string; +static void grpc_json_reader_set_string( + grpc_json_reader* reader) { + grpc_json_reader_opaque* state = reader->userdata; + grpc_json* json = grpc_json_new_and_link(reader, GRPC_JSON_STRING); + json->value = state->string; } -static int grpc_json_reader_container_set_number( - struct grpc_json_reader_t *reader) { - grpc_json *json = grpc_json_new_and_link(reader, GRPC_JSON_NUMBER); - json->value = reader->opaque.string; +static int grpc_json_reader_set_number( + grpc_json_reader* reader) { + grpc_json_reader_opaque* state = reader->userdata; + grpc_json* json = grpc_json_new_and_link(reader, GRPC_JSON_NUMBER); + json->value = 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 grpc_json_reader_container_set_true( - struct grpc_json_reader_t *reader) { +static void grpc_json_reader_set_true( + grpc_json_reader *reader) { grpc_json_new_and_link(reader, GRPC_JSON_TRUE); } -static void grpc_json_reader_container_set_false( - struct grpc_json_reader_t *reader) { +static void grpc_json_reader_set_false( + grpc_json_reader *reader) { grpc_json_new_and_link(reader, GRPC_JSON_FALSE); } -static void grpc_json_reader_container_set_null( - struct grpc_json_reader_t *reader) { +static void grpc_json_reader_set_null( + grpc_json_reader *reader) { grpc_json_new_and_link(reader, GRPC_JSON_NULL); } -/* Now that we've defined all that's needed for the parser's implementation, - * let's include its file. */ -#include "json-reader-impl.h" - /* And finally, let's define our public API. */ -grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { - struct grpc_json_reader_t reader; +grpc_json* grpc_json_parse_string_with_len(char* input, size_t size) { + grpc_json_reader reader; + grpc_json_reader_opaque state; grpc_json *json = NULL; - grpc_json_reader_ret_t status; + grpc_json_reader_ret status; if (!input) return NULL; - reader.opaque.top = reader.opaque.current_container = - reader.opaque.current_value = NULL; - reader.opaque.string = reader.opaque.key = NULL; - reader.opaque.string_ptr = reader.opaque.input = input; - reader.opaque.remaining_input = size; + state.top = state.current_container = state.current_value = NULL; + state.string = state.key = NULL; + state.string_ptr = state.input = input; + state.remaining_input = size; + reader.userdata = &state; + reader.string_clear = grpc_json_reader_string_clear; + reader.string_add_char = grpc_json_reader_string_add_char; + reader.string_add_utf32 = grpc_json_reader_string_add_utf32; + reader.read_char = grpc_json_reader_read_char; + reader.container_begins = grpc_json_reader_container_begins; + reader.container_ends = grpc_json_reader_container_ends; + reader.set_key = grpc_json_reader_set_key; + reader.set_string = grpc_json_reader_set_string; + reader.set_number = grpc_json_reader_set_number; + reader.set_true = grpc_json_reader_set_true; + reader.set_false = grpc_json_reader_set_false; + reader.set_null = grpc_json_reader_set_null; grpc_json_reader_init(&reader); status = grpc_json_reader_run(&reader); - json = reader.opaque.top; + json = state.top; if ((status != GRPC_JSON_DONE) && json) { grpc_json_delete(json); @@ -325,12 +330,14 @@ grpc_json *grpc_json_parse_string_with_len(char *input, size_t size) { return json; } -grpc_json *grpc_json_parse_string(char *input) { - return grpc_json_parse_string_with_len(input, 0x7fffffff); +#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 grpc_json_dump_recursive(struct grpc_json_writer_t *writer, - grpc_json *json, int in_object) { +static void grpc_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); @@ -365,14 +372,20 @@ static void grpc_json_dump_recursive(struct grpc_json_writer_t *writer, } } -char *grpc_json_dump_to_string(grpc_json *json, int indent) { - struct grpc_json_writer_t writer; - writer.opaque.output = NULL; - writer.opaque.free_space = writer.opaque.string_len = - writer.opaque.allocated = 0; +char* grpc_json_dump_to_string(grpc_json* json, int indent) { + grpc_json_writer writer; + grpc_json_writer_opaque state; + state.output = NULL; + state.free_space = state.string_len = state.allocated = 0; + writer.userdata = &state; + writer.output_char = grpc_json_writer_output_char; + writer.output_string = grpc_json_writer_output_string; + writer.output_string_with_len = grpc_json_writer_output_string_with_len; grpc_json_writer_init(&writer, indent); + grpc_json_dump_recursive(&writer, json, 0); + grpc_json_writer_output_char(&writer, 0); - return writer.opaque.output; + return state.output; } diff --git a/src/core/json/json-writer-impl.h b/src/core/json/json_writer.c index f3ef968bb0..9fc65aa83c 100644 --- a/src/core/json/json-writer-impl.h +++ b/src/core/json/json_writer.c @@ -31,39 +31,31 @@ * */ +#include <grpc/support/port_platform.h> +#include "src/core/json/json_writer.h" -/* 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. - * - * Also, unlike the reader, the writer expects UTF-8 encoded input strings. - * - * The following need to be defined: - * - * // Adds a character to the output stream. - * void grpc_json_writer_output_char(struct grpc_json_writer_t *, char); - * // Adds a zero-terminated string to the output stream. - * void grpc_json_writer_output_string( - * struct grpc_json_writer_t *writer, const char *str); - * // Adds a fixed-length string to the output stream. - * void grpc_json_writer_output_string_with_len( - * struct grpc_json_writer_t *writer, const char *str, size_t len); +static void grpc_json_writer_output_char(grpc_json_writer* writer, char c) { + writer->output_char(writer, c); +} - */ +static void grpc_json_writer_output_string(grpc_json_writer* writer, const char* str) { + writer->output_string(writer, str); +} + +static void grpc_json_writer_output_string_with_len(grpc_json_writer* writer, const char* str, size_t len) { + writer->output_string_with_len(writer, str, len); +} /* Call this function to initialize the writer structure. */ -grpc_json_static_inline void grpc_json_writer_init( - struct grpc_json_writer_t* writer, int indent) { +void grpc_json_writer_init(grpc_json_writer* writer, int indent) { writer->depth = 0; writer->container_empty = 1; writer->got_key = 0; writer->indent = indent; } -/* This function is fully private. */ -grpc_json_static_inline void grpc_json_writer_output_indent( - struct grpc_json_writer_t* writer) { +static void grpc_json_writer_output_indent( + grpc_json_writer* writer) { static const char spacesstr[] = " " " " @@ -89,9 +81,8 @@ grpc_json_static_inline void grpc_json_writer_output_indent( writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); } -/* This function is fully private. */ -grpc_json_static_inline void grpc_json_writer_value_end( - struct grpc_json_writer_t* writer) { +static void grpc_json_writer_value_end( + grpc_json_writer* writer) { if (writer->container_empty) { writer->container_empty = 0; if (!writer->indent || !writer->depth) return; @@ -103,10 +94,18 @@ grpc_json_static_inline void grpc_json_writer_value_end( } } -/* This function is fully private. */ -grpc_json_static_inline void grpc_json_writer_escape_string( - struct grpc_json_writer_t* writer, const char* string) { +static void grpc_json_writer_escape_utf16(grpc_json_writer* writer, gpr_uint16 utf16) { static const char hex[] = "0123456789abcdef"; + + grpc_json_writer_output_string_with_len(writer, "\\u", 2); + grpc_json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]); + grpc_json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]); + grpc_json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]); + grpc_json_writer_output_char(writer, hex[(utf16) & 0x0f]); +} + +static void grpc_json_writer_escape_string( + grpc_json_writer* writer, const char* string) { grpc_json_writer_output_char(writer, '"'); for (;;) { @@ -135,46 +134,72 @@ grpc_json_static_inline void grpc_json_writer_escape_string( grpc_json_writer_output_char(writer, 't'); break; default: - grpc_json_writer_output_string_with_len(writer, "u00", 3); - grpc_json_writer_output_char(writer, c >= 16 ? '1' : '0'); - grpc_json_writer_output_char(writer, hex[c & 15]); + grpc_json_writer_escape_utf16(writer, c); break; } } else { - unsigned unicode = 0; + gpr_uint32 utf32 = 0; + int extra = 0; + int i; + int valid = 1; if ((c & 0xe0) == 0xc0) { - unicode = c & 0x1f; - unicode <<= 6; - c = *string++; - if ((c & 0xc0) != 0x80) break; - unicode |= c & 0x3f; + utf32 = c & 0x1f; + extra = 1; } else if ((c & 0xf0) == 0xe0) { - unicode = c & 0x0f; - unicode <<= 6; - c = *string++; - if ((c & 0xc0) != 0x80) break; - unicode |= c & 0x3f; - unicode <<= 6; - c = *string++; - if ((c & 0xc0) != 0x80) break; - unicode |= c & 0x3f; + utf32 = c & 0x0f; + extra = 2; + } else if ((c & 0xf8) == 0xf0) { + utf32 = c & 0x07; + extra = 3; } else { break; } - grpc_json_writer_output_string_with_len(writer, "\\u", 2); - grpc_json_writer_output_char(writer, hex[(unicode >> 12) & 0x0f]); - grpc_json_writer_output_char(writer, hex[(unicode >> 8) & 0x0f]); - grpc_json_writer_output_char(writer, hex[(unicode >> 4) & 0x0f]); - grpc_json_writer_output_char(writer, hex[(unicode) & 0x0f]); + for (i = 0; i < extra; i++) { + utf32 <<= 6; + c = *string++; + 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; + grpc_json_writer_escape_utf16(writer, 0xd800 | (utf32 >> 10)); + grpc_json_writer_escape_utf16(writer, 0xdc00 | (utf32 && 0x3ff)); + } else { + grpc_json_writer_escape_utf16(writer, utf32); + } } } grpc_json_writer_output_char(writer, '"'); } -/* Call that function to start a new json container. */ -grpc_json_static_inline void grpc_json_writer_container_begins( - struct grpc_json_writer_t* writer, enum grpc_json_type_t type) { +void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type) { if (!writer->got_key) grpc_json_writer_value_end(writer); grpc_json_writer_output_indent(writer); grpc_json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '['); @@ -183,9 +208,7 @@ grpc_json_static_inline void grpc_json_writer_container_begins( writer->depth++; } -/* Call that function to end the current json container. */ -grpc_json_static_inline void grpc_json_writer_container_ends( - struct grpc_json_writer_t* writer, enum grpc_json_type_t type) { +void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type) { if (writer->indent && !writer->container_empty) grpc_json_writer_output_char(writer, '\n'); writer->depth--; @@ -195,9 +218,7 @@ grpc_json_static_inline void grpc_json_writer_container_ends( writer->got_key = 0; } -/* If you are in a GRPC_JSON_OBJECT container, call this to set up a key. */ -grpc_json_static_inline void grpc_json_writer_object_key( - struct grpc_json_writer_t* writer, const char* string) { +void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) { grpc_json_writer_value_end(writer); grpc_json_writer_output_indent(writer); grpc_json_writer_escape_string(writer, string); @@ -205,27 +226,21 @@ grpc_json_static_inline void grpc_json_writer_object_key( writer->got_key = 1; } -/* Sets a raw value - use it for numbers. */ -grpc_json_static_inline void grpc_json_writer_value_raw( - struct grpc_json_writer_t* writer, const char* string) { +void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) { if (!writer->got_key) grpc_json_writer_value_end(writer); grpc_json_writer_output_indent(writer); grpc_json_writer_output_string(writer, string); writer->got_key = 0; } -/* Sets a raw value with a known length - use it for true, false and null. */ -grpc_json_static_inline void grpc_json_writer_value_raw_with_len( - struct grpc_json_writer_t* writer, const char* string, unsigned len) { +void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len) { if (!writer->got_key) grpc_json_writer_value_end(writer); grpc_json_writer_output_indent(writer); grpc_json_writer_output_string_with_len(writer, string, len); writer->got_key = 0; } -/* Outputs a string value. This will add double quotes, and escape it. */ -grpc_json_static_inline void grpc_json_writer_value_string( - struct grpc_json_writer_t* writer, const char* string) { +void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string) { if (!writer->got_key) grpc_json_writer_value_end(writer); grpc_json_writer_output_indent(writer); grpc_json_writer_escape_string(writer, string); diff --git a/src/core/json/json_writer.h b/src/core/json/json_writer.h new file mode 100644 index 0000000000..f0baa1eab9 --- /dev/null +++ b/src/core/json/json_writer.h @@ -0,0 +1,92 @@ +/* + * + * Copyright 2014, 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_SRC_CORE_JSON_JSON_WRITER_H__ +#define __GRPC_SRC_CORE_JSON_JSON_WRITER_H__ + +#include <stdlib.h> + +#include "src/core/json/json_common.h" + +typedef struct grpc_json_writer { + /* You are responsible for your own opaque userdata. */ + void* userdata; + + /* The rest are your own callbacks. Define them. */ + + /* Adds a character to the output stream. */ + void (*output_char)(struct grpc_json_writer*, char); + /* Adds a zero-terminated string to the output stream. */ + void (*output_string)(struct grpc_json_writer*, const char* str); + /* Adds a fixed-length string to the output stream. */ + void (*output_string_with_len)(struct grpc_json_writer*, const char* str, size_t len); + + 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); + +/* 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_SRC_CORE_JSON_JSON_WRITER_H__ */ |