aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/json/json_writer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/json/json_writer.c')
-rw-r--r--src/core/json/json_writer.c252
1 files changed, 252 insertions, 0 deletions
diff --git a/src/core/json/json_writer.c b/src/core/json/json_writer.c
new file mode 100644
index 0000000000..63c86ac008
--- /dev/null
+++ b/src/core/json/json_writer.c
@@ -0,0 +1,252 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include <string.h>
+
+#include <grpc/support/port_platform.h>
+
+#include "src/core/json/json_writer.h"
+
+static void json_writer_output_char(grpc_json_writer* writer, char c) {
+ writer->vtable->output_char(writer->userdata, c);
+}
+
+static void json_writer_output_string(grpc_json_writer* writer, const char* str) {
+ writer->vtable->output_string(writer->userdata, str);
+}
+
+static void json_writer_output_string_with_len(grpc_json_writer* writer, const char* str, size_t len) {
+ writer->vtable->output_string_with_len(writer->userdata, str, len);
+}
+
+void grpc_json_writer_init(grpc_json_writer* writer, int indent,
+ grpc_json_writer_vtable* vtable, void* userdata) {
+ memset(writer, 0, sizeof(grpc_json_writer));
+ writer->container_empty = 1;
+ writer->indent = indent;
+ writer->vtable = vtable;
+ writer->userdata = userdata;
+}
+
+static void json_writer_output_indent(
+ grpc_json_writer* writer) {
+ static const char spacesstr[] =
+ " "
+ " "
+ " "
+ " ";
+
+ int spaces = writer->depth * writer->indent;
+
+ if (writer->indent == 0) return;
+
+ if (writer->got_key) {
+ json_writer_output_char(writer, ' ');
+ return;
+ }
+
+ while (spaces >= (sizeof(spacesstr) - 1)) {
+ json_writer_output_string_with_len(writer, spacesstr,
+ sizeof(spacesstr) - 1);
+ spaces -= (sizeof(spacesstr) - 1);
+ }
+
+ if (spaces == 0) return;
+
+ json_writer_output_string_with_len(
+ writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces);
+}
+
+static void json_writer_value_end(grpc_json_writer* writer) {
+ if (writer->container_empty) {
+ writer->container_empty = 0;
+ if ((writer->indent == 0) || (writer->depth == 0)) return;
+ json_writer_output_char(writer, '\n');
+ } else {
+ json_writer_output_char(writer, ',');
+ if (writer->indent == 0) return;
+ json_writer_output_char(writer, '\n');
+ }
+}
+
+static void json_writer_escape_utf16(grpc_json_writer* writer, gpr_uint16 utf16) {
+ static const char hex[] = "0123456789abcdef";
+
+ json_writer_output_string_with_len(writer, "\\u", 2);
+ json_writer_output_char(writer, hex[(utf16 >> 12) & 0x0f]);
+ json_writer_output_char(writer, hex[(utf16 >> 8) & 0x0f]);
+ json_writer_output_char(writer, hex[(utf16 >> 4) & 0x0f]);
+ json_writer_output_char(writer, hex[(utf16) & 0x0f]);
+}
+
+static void json_writer_escape_string(grpc_json_writer* writer,
+ const char* string) {
+ json_writer_output_char(writer, '"');
+
+ for (;;) {
+ gpr_uint8 c = (gpr_uint8)*string++;
+ if (c == 0) {
+ break;
+ } else if ((c >= 32) && (c <= 127)) {
+ if ((c == '\\') || (c == '"')) json_writer_output_char(writer, '\\');
+ json_writer_output_char(writer, c);
+ } else if (c < 32) {
+ switch (c) {
+ case '\b':
+ json_writer_output_string_with_len(writer, "\\b", 2);
+ break;
+ case '\f':
+ json_writer_output_string_with_len(writer, "\\f", 2);
+ break;
+ case '\n':
+ json_writer_output_string_with_len(writer, "\\n", 2);
+ break;
+ case '\r':
+ json_writer_output_string_with_len(writer, "\\r", 2);
+ break;
+ case '\t':
+ json_writer_output_string_with_len(writer, "\\t", 2);
+ break;
+ default:
+ json_writer_escape_utf16(writer, c);
+ break;
+ }
+ } else {
+ gpr_uint32 utf32 = 0;
+ int extra = 0;
+ int i;
+ int valid = 1;
+ if ((c & 0xe0) == 0xc0) {
+ utf32 = c & 0x1f;
+ extra = 1;
+ } else if ((c & 0xf0) == 0xe0) {
+ utf32 = c & 0x0f;
+ extra = 2;
+ } else if ((c & 0xf8) == 0xf0) {
+ utf32 = c & 0x07;
+ extra = 3;
+ } else {
+ break;
+ }
+ for (i = 0; i < extra; i++) {
+ utf32 <<= 6;
+ c = *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;
+ json_writer_escape_utf16(writer, 0xd800 | (utf32 >> 10));
+ json_writer_escape_utf16(writer, 0xdc00 | (utf32 & 0x3ff));
+ } else {
+ json_writer_escape_utf16(writer, utf32);
+ }
+ }
+ }
+
+ json_writer_output_char(writer, '"');
+}
+
+void grpc_json_writer_container_begins(grpc_json_writer* writer, grpc_json_type type) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '{' : '[');
+ writer->container_empty = 1;
+ writer->got_key = 0;
+ writer->depth++;
+}
+
+void grpc_json_writer_container_ends(grpc_json_writer* writer, grpc_json_type type) {
+ if (writer->indent && !writer->container_empty)
+ json_writer_output_char(writer, '\n');
+ writer->depth--;
+ if (!writer->container_empty) json_writer_output_indent(writer);
+ json_writer_output_char(writer, type == GRPC_JSON_OBJECT ? '}' : ']');
+ writer->container_empty = 0;
+ writer->got_key = 0;
+}
+
+void grpc_json_writer_object_key(grpc_json_writer* writer, const char* string) {
+ json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_escape_string(writer, string);
+ json_writer_output_char(writer, ':');
+ writer->got_key = 1;
+}
+
+void grpc_json_writer_value_raw(grpc_json_writer* writer, const char* string) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_output_string(writer, string);
+ writer->got_key = 0;
+}
+
+void grpc_json_writer_value_raw_with_len(grpc_json_writer* writer, const char* string, size_t len) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_output_string_with_len(writer, string, len);
+ writer->got_key = 0;
+}
+
+void grpc_json_writer_value_string(grpc_json_writer* writer, const char* string) {
+ if (!writer->got_key) json_writer_value_end(writer);
+ json_writer_output_indent(writer);
+ json_writer_escape_string(writer, string);
+ writer->got_key = 0;
+}