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/json_writer.c | |
parent | fee065c1c7f01207c0e484c92681cea184b1983a (diff) |
Addressing comments.
Diffstat (limited to 'src/core/json/json_writer.c')
-rw-r--r-- | src/core/json/json_writer.c | 248 |
1 files changed, 248 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..9fc65aa83c --- /dev/null +++ b/src/core/json/json_writer.c @@ -0,0 +1,248 @@ +/* + * + * 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 <grpc/support/port_platform.h> +#include "src/core/json/json_writer.h" + +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. */ +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; +} + +static void grpc_json_writer_output_indent( + grpc_json_writer* writer) { + static const char spacesstr[] = + " " + " " + " " + " "; + + int spaces = writer->depth * writer->indent; + + if (writer->got_key) { + grpc_json_writer_output_char(writer, ' '); + return; + } + + while (spaces >= (sizeof(spacesstr) - 1)) { + grpc_json_writer_output_string_with_len(writer, spacesstr, + sizeof(spacesstr) - 1); + spaces -= (sizeof(spacesstr) - 1); + } + + if (!spaces) return; + + grpc_json_writer_output_string_with_len( + writer, spacesstr + sizeof(spacesstr) - 1 - spaces, spaces); +} + +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; + grpc_json_writer_output_char(writer, '\n'); + } else { + grpc_json_writer_output_char(writer, ','); + if (!writer->indent) return; + grpc_json_writer_output_char(writer, '\n'); + } +} + +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 (;;) { + unsigned char c = (unsigned char)*string++; + if (!c) { + break; + } else if ((c >= 32) && (c <= 127)) { + if ((c == '\\') || (c == '"')) grpc_json_writer_output_char(writer, '\\'); + grpc_json_writer_output_char(writer, c); + } else if (c < 32) { + grpc_json_writer_output_char(writer, '\\'); + switch (c) { + case '\b': + grpc_json_writer_output_char(writer, 'b'); + break; + case '\f': + grpc_json_writer_output_char(writer, 'f'); + break; + case '\n': + grpc_json_writer_output_char(writer, 'n'); + break; + case '\r': + grpc_json_writer_output_char(writer, 'r'); + break; + case '\t': + grpc_json_writer_output_char(writer, 't'); + break; + default: + grpc_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; + 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, '"'); +} + +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 ? '{' : '['); + 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) + grpc_json_writer_output_char(writer, '\n'); + writer->depth--; + if (!writer->container_empty) grpc_json_writer_output_indent(writer); + grpc_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) { + grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_json_writer_escape_string(writer, string); + grpc_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) grpc_json_writer_value_end(writer); + grpc_json_writer_output_indent(writer); + grpc_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) 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; +} + +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); + writer->got_key = 0; +} |