aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/json
diff options
context:
space:
mode:
authorGravatar Nicolas Noble <nnoble@google.com>2015-01-26 17:01:29 -0800
committerGravatar Nicolas Noble <nnoble@google.com>2015-01-26 17:02:45 -0800
commite04455a7ff1ead05445daa95d7bc822a7f40b6f5 (patch)
treef3d7d23b6f89387d80700b1900de4453531313e1 /src/core/json
parentfee065c1c7f01207c0e484c92681cea184b1983a (diff)
Addressing comments.
Diffstat (limited to 'src/core/json')
-rw-r--r--src/core/json/json-reader-defs.h95
-rw-r--r--src/core/json/json-writer-defs.h46
-rw-r--r--src/core/json/json.c4
-rw-r--r--src/core/json/json.h22
-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.h158
-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.h92
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__ */