{"thread":{"id":"48154","subject":"[PATCH v4] routines to generate JSON data","startedAt":"2018-03-26T14:32:03Z","lastAt":"2018-06-04T09:50:30Z","messageCount":14,"participants":["git@jeffhostetler.com","Ramsay Jones","Jeff Hostetler","Wink Saville","Randall S. Becker","Junio C Hamano","Duy Nguyen","Jeff King"],"isPatch":true,"patchVersion":4,"patchTotal":null},"messages":[{"id":"343008","messageId":"20180326143136.47116-1-git@jeffhostetler.com","threadId":"48154","inReplyTo":null,"subject":"[PATCH v4] routines to generate JSON data","fromName":"","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-26T14:31:35Z","receivedAt":"2018-03-26T14:32:03Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nThis is version 4 of my JSON data format routines.\n\nThis version adds a \"pretty\" formatted output.  I consider this to be\nmainly for debugging, but worth keeping available in release builds.\n\nI simplified the stack-level tracing as suggested by René Scharfe and\nhinted at by Peff.\n\nI converted the _double() routines to take an integer precision rather\nthan a format specification and build a known-to-be-good format string\nto minimize the __attribute__(...) issues raised by René Scharfe.\n\nIt fixes the PRIuMAX and \"void inline\" compiler warnings on OSX that\nwere reported by Wink Saville and Ramsay Jones.  And resolved the \"sparse\"\nwarnings repoted by Ramsay Jones.\n\nAnd I updated the commit message and header file documnetation to address\nthe JSON-like (Unicode limitations) mentioned by Jonathan Nieder.\n\nJeff Hostetler (1):\n  json_writer: new routines to create data in JSON format\n\n Makefile                    |   2 +\n json-writer.c               | 395 +++++++++++++++++++++++++++++\n json-writer.h               |  92 +++++++\n t/helper/test-json-writer.c | 590 ++++++++++++++++++++++++++++++++++++++++++++\n t/t0019-json-writer.sh      | 253 +++++++++++++++++++\n 5 files changed, 1332 insertions(+)\n create mode 100644 json-writer.c\n create mode 100644 json-writer.h\n create mode 100644 t/helper/test-json-writer.c\n create mode 100755 t/t0019-json-writer.sh\n\n-- \n2.9.3\n\n"},{"id":"343009","messageId":"20180326143136.47116-2-git@jeffhostetler.com","threadId":"48154","inReplyTo":"20180326143136.47116-1-git@jeffhostetler.com","subject":"[PATCH v4] json_writer: new routines to create data in JSON format","fromName":"","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-26T14:31:36Z","receivedAt":"2018-03-26T14:32:05Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nAdd a series of jw_ routines and \"struct json_writer\" structure to compose\nJSON data.  The resulting string data can then be output by commands wanting\nto support a JSON output format.\n\nThe json-writer routines can be used to generate structured data in a\nJSON-like format.  We say \"JSON-like\" because we do not enforce the Unicode\n(usually UTF-8) requirement on string fields.  Internally, Git does not\nnecessarily have Unicode/UTF-8 data for most fields, so it is currently\nunclear the best way to enforce that requirement.  For example, on Linx\npathnames can contain arbitrary 8-bit character data, so a command like\n\"status\" would not know how to encode the reported pathnames.  We may want\nto revisit this (or double encode such strings) in the future.\n\nThe initial use for the json-writer routines is for generating telemetry\ndata for executed Git commands.  Later, we may want to use them in other\ncommands, such as status.\n\nHelped-by: René Scharfe <l.s.r@web.de>\nHelped-by: Wink Saville <wink@saville.com>\nHelped-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n Makefile                    |   2 +\n json-writer.c               | 395 +++++++++++++++++++++++++++++\n json-writer.h               |  92 +++++++\n t/helper/test-json-writer.c | 590 ++++++++++++++++++++++++++++++++++++++++++++\n t/t0019-json-writer.sh      | 253 +++++++++++++++++++\n 5 files changed, 1332 insertions(+)\n create mode 100644 json-writer.c\n create mode 100644 json-writer.h\n create mode 100644 t/helper/test-json-writer.c\n create mode 100755 t/t0019-json-writer.sh\n\ndiff --git a/Makefile b/Makefile\nindex 1a9b23b..57f58e6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -662,6 +662,7 @@ TEST_PROGRAMS_NEED_X += test-fake-ssh\n TEST_PROGRAMS_NEED_X += test-genrandom\n TEST_PROGRAMS_NEED_X += test-hashmap\n TEST_PROGRAMS_NEED_X += test-index-version\n+TEST_PROGRAMS_NEED_X += test-json-writer\n TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash\n TEST_PROGRAMS_NEED_X += test-line-buffer\n TEST_PROGRAMS_NEED_X += test-match-trees\n@@ -815,6 +816,7 @@ LIB_OBJS += hashmap.o\n LIB_OBJS += help.o\n LIB_OBJS += hex.o\n LIB_OBJS += ident.o\n+LIB_OBJS += json-writer.o\n LIB_OBJS += kwset.o\n LIB_OBJS += levenshtein.o\n LIB_OBJS += line-log.o\ndiff --git a/json-writer.c b/json-writer.c\nnew file mode 100644\nindex 0000000..ddcbd2a\n--- /dev/null\n+++ b/json-writer.c\n@@ -0,0 +1,395 @@\n+#include \"cache.h\"\n+#include \"json-writer.h\"\n+\n+void jw_init(struct json_writer *jw)\n+{\n+\tstrbuf_reset(&jw->json);\n+\tstrbuf_reset(&jw->open_stack);\n+\tjw->first = 0;\n+\tjw->pretty = 0;\n+}\n+\n+/*\n+ * Append JSON-quoted version of the given string to 'out'.\n+ */\n+static void append_quoted_string(struct strbuf *out, const char *in)\n+{\n+\tunsigned char c;\n+\n+\tstrbuf_addch(out, '\"');\n+\twhile ((c = *in++) != '\\0') {\n+\t\tif (c == '\"')\n+\t\t\tstrbuf_addstr(out, \"\\\\\\\"\");\n+\t\telse if (c == '\\\\')\n+\t\t\tstrbuf_addstr(out, \"\\\\\\\\\");\n+\t\telse if (c == '\\n')\n+\t\t\tstrbuf_addstr(out, \"\\\\n\");\n+\t\telse if (c == '\\r')\n+\t\t\tstrbuf_addstr(out, \"\\\\r\");\n+\t\telse if (c == '\\t')\n+\t\t\tstrbuf_addstr(out, \"\\\\t\");\n+\t\telse if (c == '\\f')\n+\t\t\tstrbuf_addstr(out, \"\\\\f\");\n+\t\telse if (c == '\\b')\n+\t\t\tstrbuf_addstr(out, \"\\\\b\");\n+\t\telse if (c < 0x20)\n+\t\t\tstrbuf_addf(out, \"\\\\u%04x\", c);\n+\t\telse\n+\t\t\tstrbuf_addch(out, c);\n+\t}\n+\tstrbuf_addch(out, '\"');\n+}\n+\n+static inline void indent_pretty(struct json_writer *jw)\n+{\n+\tint k;\n+\n+\tif (!jw->pretty)\n+\t\treturn;\n+\n+\tfor (k = 0; k < jw->open_stack.len; k++)\n+\t\tstrbuf_addstr(&jw->json, \"  \");\n+}\n+\n+static inline void begin(struct json_writer *jw, char ch_open, int pretty)\n+{\n+\tjw->pretty = pretty;\n+\tjw->first = 1;\n+\n+\tstrbuf_addch(&jw->json, ch_open);\n+\n+\tstrbuf_addch(&jw->open_stack, ch_open);\n+}\n+\n+/*\n+ * Assert that the top of the open-stack is an object.\n+ */\n+static inline void assert_in_object(const struct json_writer *jw, const char *key)\n+{\n+\tif (!jw->open_stack.len)\n+\t\tdie(\"json-writer: object: missing jw_object_begin(): '%s'\", key);\n+\tif (jw->open_stack.buf[jw->open_stack.len - 1] != '{')\n+\t\tdie(\"json-writer: object: not in object: '%s'\", key);\n+}\n+\n+/*\n+ * Assert that the top of the open-stack is an array.\n+ */\n+static inline void assert_in_array(const struct json_writer *jw)\n+{\n+\tif (!jw->open_stack.len)\n+\t\tdie(\"json-writer: array: missing jw_array_begin()\");\n+\tif (jw->open_stack.buf[jw->open_stack.len - 1] != '[')\n+\t\tdie(\"json-writer: array: not in array\");\n+}\n+\n+/*\n+ * Add comma if we have already seen a member at this level.\n+ */\n+static inline void maybe_add_comma(struct json_writer *jw)\n+{\n+\tif (!jw->open_stack.len)\n+\t\treturn;\n+\tif (jw->first)\n+\t\tjw->first = 0;\n+\telse\n+\t\tstrbuf_addch(&jw->json, ',');\n+}\n+\n+static inline void fmt_double(struct json_writer *jw, int precision,\n+\t\t\t      double value)\n+{\n+\tif (precision < 0) {\n+\t\tstrbuf_addf(&jw->json, \"%f\", value);\n+\t} else {\n+\t\tstruct strbuf fmt = STRBUF_INIT;\n+\t\tstrbuf_addf(&fmt, \"%%.%df\", precision);\n+\t\tstrbuf_addf(&jw->json, fmt.buf, value);\n+\t\tstrbuf_release(&fmt);\n+\t}\n+}\n+\n+static inline void object_common(struct json_writer *jw, const char *key)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tif (jw->pretty)\n+\t\tstrbuf_addch(&jw->json, '\\n');\n+\tindent_pretty(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addch(&jw->json, ':');\n+\tif (jw->pretty)\n+\t\tstrbuf_addch(&jw->json, ' ');\n+}\n+\n+static inline void array_common(struct json_writer *jw)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tif (jw->pretty)\n+\t\tstrbuf_addch(&jw->json, '\\n');\n+\tindent_pretty(jw);\n+}\n+\n+/*\n+ * Assert that the given JSON object or JSON array has been properly\n+ * terminated.  (Has closing bracket.)\n+ */\n+static inline void assert_is_terminated(const struct json_writer *jw)\n+{\n+\tif (jw->open_stack.len)\n+\t\tdie(\"json-writer: object: missing jw_end(): '%s'\",\n+\t\t    jw->json.buf);\n+}\n+\n+void jw_object_begin(struct json_writer *jw, int pretty)\n+{\n+\tbegin(jw, '{', pretty);\n+}\n+\n+void jw_object_string(struct json_writer *jw, const char *key, const char *value)\n+{\n+\tobject_common(jw, key);\n+\tappend_quoted_string(&jw->json, value);\n+}\n+\n+void jw_object_int(struct json_writer *jw, const char *key, int value)\n+{\n+\tobject_common(jw, key);\n+\tstrbuf_addf(&jw->json, \"%d\", value);\n+}\n+\n+void jw_object_uint64(struct json_writer *jw, const char *key, uint64_t value)\n+{\n+\tobject_common(jw, key);\n+\tstrbuf_addf(&jw->json, \"%\"PRIuMAX, (uintmax_t)value);\n+}\n+\n+void jw_object_double(struct json_writer *jw, const char *key, int precision,\n+\t\t      double value)\n+{\n+\tobject_common(jw, key);\n+\tfmt_double(jw, precision, value);\n+}\n+\n+void jw_object_true(struct json_writer *jw, const char *key)\n+{\n+\tobject_common(jw, key);\n+\tstrbuf_addstr(&jw->json, \"true\");\n+}\n+\n+void jw_object_false(struct json_writer *jw, const char *key)\n+{\n+\tobject_common(jw, key);\n+\tstrbuf_addstr(&jw->json, \"false\");\n+}\n+\n+void jw_object_null(struct json_writer *jw, const char *key)\n+{\n+\tobject_common(jw, key);\n+\tstrbuf_addstr(&jw->json, \"null\");\n+}\n+\n+static void increase_indent(struct strbuf *sb,\n+\t\t\t    const struct json_writer *jw,\n+\t\t\t    int indent)\n+{\n+\tint k;\n+\n+\tstrbuf_reset(sb);\n+\tfor (k = 0; k < jw->json.len; k++) {\n+\t\tchar ch = jw->json.buf[k];\n+\t\tstrbuf_addch(sb, ch);\n+\t\tif (ch == '\\n')\n+\t\t\tstrbuf_addchars(sb, ' ', indent);\n+\t}\n+}\n+\n+static void kill_indent(struct strbuf *sb,\n+\t\t\tconst struct json_writer *jw)\n+{\n+\tint k;\n+\tint eat_it = 0;\n+\n+\tstrbuf_reset(sb);\n+\tfor (k = 0; k < jw->json.len; k++) {\n+\t\tchar ch = jw->json.buf[k];\n+\t\tif (eat_it && ch == ' ')\n+\t\t\tcontinue;\n+\t\tif (ch == '\\n') {\n+\t\t\teat_it = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\teat_it = 0;\n+\t\tstrbuf_addch(sb, ch);\n+\t}\n+}\n+\n+/*\n+ * Append existing (properly terminated) JSON sub-data (object or array)\n+ * as-is onto the given JSON data.\n+ */\n+void jw_object_sub_jw(struct json_writer *jw, const char *key,\n+\t\t      const struct json_writer *value)\n+{\n+\tassert_is_terminated(value);\n+\n+\tobject_common(jw, key);\n+\n+\t/*\n+\t * If both are pretty, increase the indentation of the sub_jw\n+\t * to better fit under the super.\n+\t *\n+\t * If the super is pretty, but the sub_jw is compact, leave the\n+\t * sub_jw compact.  (We don't want to parse and rebuild the sub_jw\n+\t * for this debug-ish feature.)\n+\t *\n+\t * If the super is compact, and the sub_jw is pretty, convert\n+\t * the sub_jw to compact.\n+\t *\n+\t * If both are compact, keep the sub_jw compact.\n+\t */\n+\tif (jw->pretty && jw->open_stack.len && value->pretty) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tincrease_indent(&sb, value, jw->open_stack.len * 2);\n+\t\tstrbuf_addbuf(&jw->json, &sb);\n+\t\tstrbuf_release(&sb);\n+\t\treturn;\n+\t}\n+\tif (!jw->pretty && value->pretty) {\n+\t\tstruct strbuf sb = STRBUF_INIT;\n+\t\tkill_indent(&sb, value);\n+\t\tstrbuf_addbuf(&jw->json, &sb);\n+\t\tstrbuf_release(&sb);\n+\t\treturn;\n+\t}\n+\n+\tstrbuf_addbuf(&jw->json, &value->json);\n+}\n+\n+void jw_object_inline_begin_object(struct json_writer *jw, const char *key)\n+{\n+\tobject_common(jw, key);\n+\n+\tjw_object_begin(jw, jw->pretty);\n+}\n+\n+void jw_object_inline_begin_array(struct json_writer *jw, const char *key)\n+{\n+\tobject_common(jw, key);\n+\n+\tjw_array_begin(jw, jw->pretty);\n+}\n+\n+void jw_array_begin(struct json_writer *jw, int pretty)\n+{\n+\tbegin(jw, '[', pretty);\n+}\n+\n+void jw_array_string(struct json_writer *jw, const char *value)\n+{\n+\tarray_common(jw);\n+\tappend_quoted_string(&jw->json, value);\n+}\n+\n+void jw_array_int(struct json_writer *jw,int value)\n+{\n+\tarray_common(jw);\n+\tstrbuf_addf(&jw->json, \"%d\", value);\n+}\n+\n+void jw_array_uint64(struct json_writer *jw, uint64_t value)\n+{\n+\tarray_common(jw);\n+\tstrbuf_addf(&jw->json, \"%\"PRIuMAX, (uintmax_t)value);\n+}\n+\n+void jw_array_double(struct json_writer *jw, int precision, double value)\n+{\n+\tarray_common(jw);\n+\tfmt_double(jw, precision, value);\n+}\n+\n+void jw_array_true(struct json_writer *jw)\n+{\n+\tarray_common(jw);\n+\tstrbuf_addstr(&jw->json, \"true\");\n+}\n+\n+void jw_array_false(struct json_writer *jw)\n+{\n+\tarray_common(jw);\n+\tstrbuf_addstr(&jw->json, \"false\");\n+}\n+\n+void jw_array_null(struct json_writer *jw)\n+{\n+\tarray_common(jw);\n+\tstrbuf_addstr(&jw->json, \"null\");\n+}\n+\n+void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value)\n+{\n+\tassert_is_terminated(value);\n+\n+\tarray_common(jw);\n+\tstrbuf_addbuf(&jw->json, &value->json);\n+}\n+\n+void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv)\n+{\n+\tint k;\n+\n+\tfor (k = 0; k < argc; k++)\n+\t\tjw_array_string(jw, argv[k]);\n+}\n+\n+void jw_array_argv(struct json_writer *jw, const char **argv)\n+{\n+\twhile (*argv)\n+\t\tjw_array_string(jw, *argv++);\n+}\n+\n+void jw_array_inline_begin_object(struct json_writer *jw)\n+{\n+\tarray_common(jw);\n+\n+\tjw_object_begin(jw, jw->pretty);\n+}\n+\n+void jw_array_inline_begin_array(struct json_writer *jw)\n+{\n+\tarray_common(jw);\n+\n+\tjw_array_begin(jw, jw->pretty);\n+}\n+\n+int jw_is_terminated(const struct json_writer *jw)\n+{\n+\treturn !jw->open_stack.len;\n+}\n+\n+void jw_end(struct json_writer *jw)\n+{\n+\tchar ch_open;\n+\tint len;\n+\n+\tif (!jw->open_stack.len)\n+\t\tdie(\"json-writer: too many jw_end(): '%s'\", jw->json.buf);\n+\n+\tlen = jw->open_stack.len - 1;\n+\tch_open = jw->open_stack.buf[len];\n+\tstrbuf_setlen(&jw->open_stack, len);\n+\n+\tif (jw->pretty)\n+\t\tstrbuf_addch(&jw->json, '\\n');\n+\tindent_pretty(jw);\n+\n+\tif (ch_open == '{')\n+\t\tstrbuf_addch(&jw->json, '}');\n+\telse\n+\t\tstrbuf_addch(&jw->json, ']');\n+}\ndiff --git a/json-writer.h b/json-writer.h\nnew file mode 100644\nindex 0000000..16376ee\n--- /dev/null\n+++ b/json-writer.h\n@@ -0,0 +1,92 @@\n+#ifndef JSON_WRITER_H\n+#define JSON_WRITER_H\n+\n+/*\n+ * JSON data structures are defined at:\n+ * [1] http://www.ietf.org/rfc/rfc7159.txt\n+ * [2] http://json.org/\n+ *\n+ * The JSON-writer API allows one to build JSON data structures using a\n+ * simple wrapper around a \"struct strbuf\" buffer.  It is intended as a\n+ * simple API to build output strings; it is not intended to be a general\n+ * object model for JSON data.  In particular, it does not re-order keys\n+ * in an object (dictionary), it does not de-dup keys in an object, and\n+ * it does not allow lookup or parsing of JSON data.\n+ *\n+ * All string values (both keys and string r-values) are properly quoted\n+ * and escaped if they contain special characters.\n+ *\n+ * These routines create compact JSON data (with no unnecessary whitespace,\n+ * newlines, or indenting).  If you get an unexpected response, verify\n+ * that you're not expecting a pretty JSON string.\n+ *\n+ * Both \"JSON objects\" (aka sets of k/v pairs) and \"JSON array\" can be\n+ * constructed using a 'begin append* end' model.\n+ *\n+ * Nested objects and arrays can either be constructed bottom up (by\n+ * creating sub object/arrays first and appending them to the super\n+ * object/array) -or- by building them inline in one pass.  This is a\n+ * personal style and/or data shape choice.\n+ *\n+ * See t/helper/test-json-writer.c for various usage examples.\n+ *\n+ * LIMITATIONS:\n+ * ============\n+ *\n+ * The JSON specification [1,2] defines string values as Unicode data\n+ * and probably UTF-8 encoded.  The current json-writer API does not\n+ * enforce this and will write any string as received.  However, it will\n+ * properly quote and backslash-escape them as necessary.  It is up to\n+ * the caller to UTF-8 encode their strings *before* passing them to this\n+ * API.  This layer should not have to try to guess the encoding or locale\n+ * of the given strings.\n+ */\n+\n+struct json_writer\n+{\n+\tstruct strbuf json;\n+\tstruct strbuf open_stack;\n+\tint first;\n+\tint pretty;\n+};\n+\n+#define JSON_WRITER_INIT { STRBUF_INIT, STRBUF_INIT, 0, 0 }\n+\n+void jw_init(struct json_writer *jw);\n+\n+void jw_object_begin(struct json_writer *jw, int pretty);\n+void jw_array_begin(struct json_writer *jw, int pretty);\n+\n+void jw_object_string(struct json_writer *jw, const char *key,\n+\t\t      const char *value);\n+void jw_object_int(struct json_writer *jw, const char *key, int value);\n+void jw_object_uint64(struct json_writer *jw, const char *key, uint64_t value);\n+void jw_object_double(struct json_writer *jw, const char *key, int precision,\n+\t\t      double value);\n+void jw_object_true(struct json_writer *jw, const char *key);\n+void jw_object_false(struct json_writer *jw, const char *key);\n+void jw_object_null(struct json_writer *jw, const char *key);\n+void jw_object_sub_jw(struct json_writer *jw, const char *key,\n+\t\t      const struct json_writer *value);\n+\n+void jw_object_inline_begin_object(struct json_writer *jw, const char *key);\n+void jw_object_inline_begin_array(struct json_writer *jw, const char *key);\n+\n+void jw_array_string(struct json_writer *jw, const char *value);\n+void jw_array_int(struct json_writer *jw,int value);\n+void jw_array_uint64(struct json_writer *jw, uint64_t value);\n+void jw_array_double(struct json_writer *jw, int precision, double value);\n+void jw_array_true(struct json_writer *jw);\n+void jw_array_false(struct json_writer *jw);\n+void jw_array_null(struct json_writer *jw);\n+void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value);\n+void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv);\n+void jw_array_argv(struct json_writer *jw, const char **argv);\n+\n+void jw_array_inline_begin_object(struct json_writer *jw);\n+void jw_array_inline_begin_array(struct json_writer *jw);\n+\n+int jw_is_terminated(const struct json_writer *jw);\n+void jw_end(struct json_writer *jw);\n+\n+#endif /* JSON_WRITER_H */\ndiff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c\nnew file mode 100644\nindex 0000000..0f50c67\n--- /dev/null\n+++ b/t/helper/test-json-writer.c\n@@ -0,0 +1,590 @@\n+#include \"cache.h\"\n+#include \"json-writer.h\"\n+\n+static const char *expect_obj1 = \"{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true}\";\n+static const char *expect_obj2 = \"{\\\"a\\\":-1,\\\"b\\\":2147483647,\\\"c\\\":0}\";\n+static const char *expect_obj3 = \"{\\\"a\\\":0,\\\"b\\\":4294967295,\\\"c\\\":18446744073709551615}\";\n+static const char *expect_obj4 = \"{\\\"t\\\":true,\\\"f\\\":false,\\\"n\\\":null}\";\n+static const char *expect_obj5 = \"{\\\"abc\\\\tdef\\\":\\\"abc\\\\\\\\def\\\"}\";\n+static const char *expect_obj6 = \"{\\\"a\\\":3.14}\";\n+\n+static const char *pretty_obj1 = (\"{\\n\"\n+\t\t\t\t  \"  \\\"a\\\": \\\"abc\\\",\\n\"\n+\t\t\t\t  \"  \\\"b\\\": 42,\\n\"\n+\t\t\t\t  \"  \\\"c\\\": true\\n\"\n+\t\t\t\t  \"}\");\n+static const char *pretty_obj2 = (\"{\\n\"\n+\t\t\t\t  \"  \\\"a\\\": -1,\\n\"\n+\t\t\t\t  \"  \\\"b\\\": 2147483647,\\n\"\n+\t\t\t\t  \"  \\\"c\\\": 0\\n\"\n+\t\t\t\t  \"}\");\n+static const char *pretty_obj3 = (\"{\\n\"\n+\t\t\t\t  \"  \\\"a\\\": 0,\\n\"\n+\t\t\t\t  \"  \\\"b\\\": 4294967295,\\n\"\n+\t\t\t\t  \"  \\\"c\\\": 18446744073709551615\\n\"\n+\t\t\t\t  \"}\");\n+static const char *pretty_obj4 = (\"{\\n\"\n+\t\t\t\t  \"  \\\"t\\\": true,\\n\"\n+\t\t\t\t  \"  \\\"f\\\": false,\\n\"\n+\t\t\t\t  \"  \\\"n\\\": null\\n\"\n+\t\t\t\t  \"}\");\n+\n+static struct json_writer obj1 = JSON_WRITER_INIT;\n+static struct json_writer obj2 = JSON_WRITER_INIT;\n+static struct json_writer obj3 = JSON_WRITER_INIT;\n+static struct json_writer obj4 = JSON_WRITER_INIT;\n+static struct json_writer obj5 = JSON_WRITER_INIT;\n+static struct json_writer obj6 = JSON_WRITER_INIT;\n+\n+static void make_obj1(int pretty)\n+{\n+\tjw_object_begin(&obj1, pretty);\n+\t{\n+\t\tjw_object_string(&obj1, \"a\", \"abc\");\n+\t\tjw_object_int(&obj1, \"b\", 42);\n+\t\tjw_object_true(&obj1, \"c\");\n+\t}\n+\tjw_end(&obj1);\n+}\n+\n+static void make_obj2(int pretty)\n+{\n+\tjw_object_begin(&obj2, pretty);\n+\t{\n+\t\tjw_object_int(&obj2, \"a\", -1);\n+\t\tjw_object_int(&obj2, \"b\", 0x7fffffff);\n+\t\tjw_object_int(&obj2, \"c\", 0);\n+\t}\n+\tjw_end(&obj2);\n+}\n+\n+static void make_obj3(int pretty)\n+{\n+\tjw_object_begin(&obj3, pretty);\n+\t{\n+\t\tjw_object_uint64(&obj3, \"a\", 0);\n+\t\tjw_object_uint64(&obj3, \"b\", 0xffffffff);\n+\t\tjw_object_uint64(&obj3, \"c\", 0xffffffffffffffffULL);\n+\t}\n+\tjw_end(&obj3);\n+}\n+\n+static void make_obj4(int pretty)\n+{\n+\tjw_object_begin(&obj4, pretty);\n+\t{\n+\t\tjw_object_true(&obj4, \"t\");\n+\t\tjw_object_false(&obj4, \"f\");\n+\t\tjw_object_null(&obj4, \"n\");\n+\t}\n+\tjw_end(&obj4);\n+}\n+\n+static void make_obj5(int pretty)\n+{\n+\tjw_object_begin(&obj5, pretty);\n+\t{\n+\t\tjw_object_string(&obj5, \"abc\" \"\\x09\" \"def\", \"abc\" \"\\\\\" \"def\");\n+\t}\n+\tjw_end(&obj5);\n+}\n+\n+static void make_obj6(int pretty)\n+{\n+\tjw_object_begin(&obj6, pretty);\n+\t{\n+\t\tjw_object_double(&obj6, \"a\", 2, 3.14159);\n+\t}\n+\tjw_end(&obj6);\n+}\n+\n+static const char *expect_arr1 = \"[\\\"abc\\\",42,true]\";\n+static const char *expect_arr2 = \"[-1,2147483647,0]\";\n+static const char *expect_arr3 = \"[0,4294967295,18446744073709551615]\";\n+static const char *expect_arr4 = \"[true,false,null]\";\n+\n+static const char *pretty_arr1 = (\"[\\n\"\n+\t\t\t\t  \"  \\\"abc\\\",\\n\"\n+\t\t\t\t  \"  42,\\n\"\n+\t\t\t\t  \"  true\\n\"\n+\t\t\t\t  \"]\");\n+static const char *pretty_arr2 = (\"[\\n\"\n+\t\t\t\t  \"  -1,\\n\"\n+\t\t\t\t  \"  2147483647,\\n\"\n+\t\t\t\t  \"  0\\n\"\n+\t\t\t\t  \"]\");\n+static const char *pretty_arr3 = (\"[\\n\"\n+\t\t\t\t  \"  0,\\n\"\n+\t\t\t\t  \"  4294967295,\\n\"\n+\t\t\t\t  \"  18446744073709551615\\n\"\n+\t\t\t\t  \"]\");\n+static const char *pretty_arr4 = (\"[\\n\"\n+\t\t\t\t  \"  true,\\n\"\n+\t\t\t\t  \"  false,\\n\"\n+\t\t\t\t  \"  null\\n\"\n+\t\t\t\t  \"]\");\n+\n+static struct json_writer arr1 = JSON_WRITER_INIT;\n+static struct json_writer arr2 = JSON_WRITER_INIT;\n+static struct json_writer arr3 = JSON_WRITER_INIT;\n+static struct json_writer arr4 = JSON_WRITER_INIT;\n+\n+static void make_arr1(int pretty)\n+{\n+\tjw_array_begin(&arr1, pretty);\n+\t{\n+\t\tjw_array_string(&arr1, \"abc\");\n+\t\tjw_array_int(&arr1, 42);\n+\t\tjw_array_true(&arr1);\n+\t}\n+\tjw_end(&arr1);\n+}\n+\n+static void make_arr2(int pretty)\n+{\n+\tjw_array_begin(&arr2, pretty);\n+\t{\n+\t\tjw_array_int(&arr2, -1);\n+\t\tjw_array_int(&arr2, 0x7fffffff);\n+\t\tjw_array_int(&arr2, 0);\n+\t}\n+\tjw_end(&arr2);\n+}\n+\n+static void make_arr3(int pretty)\n+{\n+\tjw_array_begin(&arr3, pretty);\n+\t{\n+\t\tjw_array_uint64(&arr3, 0);\n+\t\tjw_array_uint64(&arr3, 0xffffffff);\n+\t\tjw_array_uint64(&arr3, 0xffffffffffffffffULL);\n+\t}\n+\tjw_end(&arr3);\n+}\n+\n+static void make_arr4(int pretty)\n+{\n+\tjw_array_begin(&arr4, pretty);\n+\t{\n+\t\tjw_array_true(&arr4);\n+\t\tjw_array_false(&arr4);\n+\t\tjw_array_null(&arr4);\n+\t}\n+\tjw_end(&arr4);\n+}\n+\n+static char *expect_nest1 =\n+\t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n+\n+static struct json_writer nest1 = JSON_WRITER_INIT;\n+\n+static void make_nest1(int pretty)\n+{\n+\tjw_object_begin(&nest1, pretty);\n+\t{\n+\t\tjw_object_sub_jw(&nest1, \"obj1\", &obj1);\n+\t\tjw_object_sub_jw(&nest1, \"arr1\", &arr1);\n+\t}\n+\tjw_end(&nest1);\n+}\n+\n+static char *expect_inline1 =\n+\t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n+\n+static char *pretty_inline1 =\n+\t(\"{\\n\"\n+\t \"  \\\"obj1\\\": {\\n\"\n+\t \"    \\\"a\\\": \\\"abc\\\",\\n\"\n+\t \"    \\\"b\\\": 42,\\n\"\n+\t \"    \\\"c\\\": true\\n\"\n+\t \"  },\\n\"\n+\t \"  \\\"arr1\\\": [\\n\"\n+\t \"    \\\"abc\\\",\\n\"\n+\t \"    42,\\n\"\n+\t \"    true\\n\"\n+\t \"  ]\\n\"\n+\t \"}\");\n+\n+static struct json_writer inline1 = JSON_WRITER_INIT;\n+\n+static void make_inline1(int pretty)\n+{\n+\tjw_object_begin(&inline1, pretty);\n+\t{\n+\t\tjw_object_inline_begin_object(&inline1, \"obj1\");\n+\t\t{\n+\t\t\tjw_object_string(&inline1, \"a\", \"abc\");\n+\t\t\tjw_object_int(&inline1, \"b\", 42);\n+\t\t\tjw_object_true(&inline1, \"c\");\n+\t\t}\n+\t\tjw_end(&inline1);\n+\t\tjw_object_inline_begin_array(&inline1, \"arr1\");\n+\t\t{\n+\t\t\tjw_array_string(&inline1, \"abc\");\n+\t\t\tjw_array_int(&inline1, 42);\n+\t\t\tjw_array_true(&inline1);\n+\t\t}\n+\t\tjw_end(&inline1);\n+\t}\n+\tjw_end(&inline1);\n+}\n+\n+static char *expect_inline2 =\n+\t\"[[1,2],[3,4],{\\\"a\\\":\\\"abc\\\"}]\";\n+\n+static char *pretty_inline2 =\n+\t(\"[\\n\"\n+\t \"  [\\n\"\n+\t \"    1,\\n\"\n+\t \"    2\\n\"\n+\t \"  ],\\n\"\n+\t \"  [\\n\"\n+\t \"    3,\\n\"\n+\t \"    4\\n\"\n+\t \"  ],\\n\"\n+\t \"  {\\n\"\n+\t \"    \\\"a\\\": \\\"abc\\\"\\n\"\n+\t \"  }\\n\"\n+\t \"]\");\n+\n+static struct json_writer inline2 = JSON_WRITER_INIT;\n+\n+static void make_inline2(int pretty)\n+{\n+\tjw_array_begin(&inline2, pretty);\n+\t{\n+\t\tjw_array_inline_begin_array(&inline2);\n+\t\t{\n+\t\t\tjw_array_int(&inline2, 1);\n+\t\t\tjw_array_int(&inline2, 2);\n+\t\t}\n+\t\tjw_end(&inline2);\n+\t\tjw_array_inline_begin_array(&inline2);\n+\t\t{\n+\t\t\tjw_array_int(&inline2, 3);\n+\t\t\tjw_array_int(&inline2, 4);\n+\t\t}\n+\t\tjw_end(&inline2);\n+\t\tjw_array_inline_begin_object(&inline2);\n+\t\t{\n+\t\t\tjw_object_string(&inline2, \"a\", \"abc\");\n+\t\t}\n+\t\tjw_end(&inline2);\n+\t}\n+\tjw_end(&inline2);\n+}\n+\n+/*\n+ * When super is compact, we expect subs to be compacted (even if originally\n+ * pretty).\n+ */ \n+static const char *expect_mixed1 =\n+\t(\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\"\n+\t \"\\\"arr1\\\":[\\\"abc\\\",42,true]}\");\n+\n+/*\n+ * When super is pretty, a compact sub (obj1) is kept compact and a pretty\n+ * sub (arr1) is re-indented.\n+ */\n+static const char *pretty_mixed1 =\n+\t(\"{\\n\"\n+\t \"  \\\"obj1\\\": {\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\n\"\n+\t \"  \\\"arr1\\\": [\\n\"\n+\t \"    \\\"abc\\\",\\n\"\n+\t \"    42,\\n\"\n+\t \"    true\\n\"\n+\t \"  ]\\n\"\n+\t \"}\");\n+\n+static struct json_writer mixed1 = JSON_WRITER_INIT;\n+\n+static void make_mixed1(int pretty)\n+{\n+\tjw_init(&obj1);\n+\tjw_init(&arr1);\n+\n+\tmake_obj1(0); /* obj1 is compact */\n+\tmake_arr1(1); /* arr1 is pretty */\n+\n+\tjw_object_begin(&mixed1, pretty);\n+\t{\n+\t\tjw_object_sub_jw(&mixed1, \"obj1\", &obj1);\n+\t\tjw_object_sub_jw(&mixed1, \"arr1\", &arr1);\n+\t}\n+\tjw_end(&mixed1);\n+}\n+\n+static void cmp(const char *test, const struct json_writer *jw, const char *exp)\n+{\n+\tif (!strcmp(jw->json.buf, exp))\n+\t\treturn;\n+\n+\tprintf(\"error[%s]: observed '%s' expected '%s'\\n\",\n+\t       test, jw->json.buf, exp);\n+\texit(1);\n+}\n+\n+#define t(v) do { make_##v(0); cmp(#v, &v, expect_##v); } while (0)\n+#define p(v) do { make_##v(1); cmp(#v, &v, pretty_##v); } while (0)\n+\n+/*\n+ * Run some basic regression tests with some known patterns.\n+ * These tests also demonstrate how to use the jw_ API.\n+ */\n+static int unit_tests(void)\n+{\n+\t/* comptact (canonical) forms */\n+\tt(obj1);\n+\tt(obj2);\n+\tt(obj3);\n+\tt(obj4);\n+\tt(obj5);\n+\tt(obj6);\n+\n+\tt(arr1);\n+\tt(arr2);\n+\tt(arr3);\n+\tt(arr4);\n+\n+\tt(nest1);\n+\n+\tt(inline1);\n+\tt(inline2);\n+\n+\tjw_init(&obj1);\n+\tjw_init(&obj2);\n+\tjw_init(&obj3);\n+\tjw_init(&obj4);\n+\n+\tjw_init(&arr1);\n+\tjw_init(&arr2);\n+\tjw_init(&arr3);\n+\tjw_init(&arr4);\n+\n+\tjw_init(&inline1);\n+\tjw_init(&inline2);\n+\n+\t/* pretty forms */\n+\tp(obj1);\n+\tp(obj2);\n+\tp(obj3);\n+\tp(obj4);\n+\n+\tp(arr1);\n+\tp(arr2);\n+\tp(arr3);\n+\tp(arr4);\n+\n+\tp(inline1);\n+\tp(inline2);\n+\n+\t/* mixed forms */\n+\tt(mixed1);\n+\tjw_init(&mixed1);\n+\tp(mixed1);\n+\n+\treturn 0;\n+}\n+\n+#define STMT(s) do { s } while (0)\n+\n+#define PARAM(tok, lbl, p) \\\n+\tSTMT( if (!(p) || (*(p) == '@')) \\\n+\t\tdie(\"token '%s' requires '%s' parameter, but saw: '%s'\", \\\n+\t\t    tok, lbl, p); )\n+\n+#define KEY() \\\n+\tSTMT( key = argv[++k]; \\\n+\t      PARAM(a_k, \"key\", key); )\n+\n+#define VAL() \\\n+\tSTMT( val = argv[++k]; \\\n+\t      PARAM(a_k, \"val\", val); )\n+\n+#define PRECISION() \\\n+\tSTMT( const char *s = argv[++k]; \\\n+\t      PARAM(a_k, \"precision\", s); \\\n+\t      precision = strtol(s, &endptr, 10); \\\n+\t      if (*endptr || errno == ERANGE) \\\n+\t\tdie(\"invalid '%s' precision: '%s'\", a_k, s); )\n+\n+#define VAL_INT() \\\n+\tSTMT( VAL(); \\\n+\t      v_int = strtol(val, &endptr, 10); \\\n+\t      if (*endptr || errno == ERANGE) \\\n+\t\tdie(\"invalid '%s' value: '%s'\",\ta_k, val); )\n+\n+#define VAL_UINT64() \\\n+\tSTMT( VAL(); \\\n+\t      v_uint64 = strtoull(val, &endptr, 10); \\\n+\t      if (*endptr || errno == ERANGE) \\\n+\t\tdie(\"invalid '%s' value: '%s'\",\ta_k, val); )\n+\n+#define VAL_DOUBLE() \\\n+\tSTMT( VAL(); \\\n+\t      v_double = strtod(val, &endptr); \\\n+\t      if (*endptr || errno == ERANGE) \\\n+\t\tdie(\"invalid '%s' value: '%s'\",\ta_k, val); )\n+\n+static int pretty;\n+\n+static inline int scripted(int argc, const char **argv)\n+{\n+\tstruct json_writer jw = JSON_WRITER_INIT;\n+\tint k;\n+\n+\tif (!strcmp(argv[0], \"@object\"))\n+\t\tjw_object_begin(&jw, pretty);\n+\telse if (!strcmp(argv[0], \"@array\"))\n+\t\tjw_array_begin(&jw, pretty);\n+\telse\n+\t\tdie(\"expected '@object' or '@array': '%s'\", argv[0]);\n+\n+\tfor (k = 1; k < argc; k++) {\n+\t\tconst char *a_k = argv[k];\n+\t\tconst char *key;\n+\t\tconst char *val;\n+\t\tchar *endptr;\n+\t\tint precision;\n+\t\tint v_int;\n+\t\tuint64_t v_uint64;\n+\t\tdouble v_double;\n+\n+\t\tif (!strcmp(a_k, \"@end\")) {\n+\t\t\tjw_end(&jw);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (!strcmp(a_k, \"@object-string\")) {\n+\t\t\tKEY();\n+\t\t\tVAL();\n+\t\t\tjw_object_string(&jw, key, val);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-int\")) {\n+\t\t\tKEY();\n+\t\t\tVAL_INT();\n+\t\t\tjw_object_int(&jw, key, v_int);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-uint64\")) {\n+\t\t\tKEY();\n+\t\t\tVAL_UINT64();\n+\t\t\tjw_object_uint64(&jw, key, v_uint64);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-double\")) {\n+\t\t\tKEY();\n+\t\t\tPRECISION();\n+\t\t\tVAL_DOUBLE();\n+\t\t\tjw_object_double(&jw, key, precision, v_double);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-true\")) {\n+\t\t\tKEY();\n+\t\t\tjw_object_true(&jw, key);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-false\")) {\n+\t\t\tKEY();\n+\t\t\tjw_object_false(&jw, key);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-null\")) {\n+\t\t\tKEY();\n+\t\t\tjw_object_null(&jw, key);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-object\")) {\n+\t\t\tKEY();\n+\t\t\tjw_object_inline_begin_object(&jw, key);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@object-array\")) {\n+\t\t\tKEY();\n+\t\t\tjw_object_inline_begin_array(&jw, key);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tif (!strcmp(a_k, \"@array-string\")) {\n+\t\t\tVAL();\n+\t\t\tjw_array_string(&jw, val);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-int\")) {\n+\t\t\tVAL_INT();\n+\t\t\tjw_array_int(&jw, v_int);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-uint64\")) {\n+\t\t\tVAL_UINT64();\n+\t\t\tjw_array_uint64(&jw, v_uint64);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-double\")) {\n+\t\t\tPRECISION();\n+\t\t\tVAL_DOUBLE();\n+\t\t\tjw_array_double(&jw, precision, v_double);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-true\")) {\n+\t\t\tjw_array_true(&jw);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-false\")) {\n+\t\t\tjw_array_false(&jw);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-null\")) {\n+\t\t\tjw_array_null(&jw);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-object\")) {\n+\t\t\tjw_array_inline_begin_object(&jw);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tif (!strcmp(a_k, \"@array-array\")) {\n+\t\t\tjw_array_inline_begin_array(&jw);\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tdie(\"unrecognized token: '%s'\", a_k);\n+\t}\n+\n+\tif (!jw_is_terminated(&jw))\n+\t\tdie(\"json not terminated: '%s'\", jw.json.buf);\n+\n+\tprintf(\"%s\\n\", jw.json.buf);\n+\n+\tstrbuf_release(&jw.json);\n+\treturn 0;\n+}\n+\n+static inline int my_usage(void)\n+{\n+\tdie(\"usage: '-u' | '[-p] @object ... @end' | '[-p] @array ... @end'\");\n+}\n+\n+int cmd_main(int argc, const char **argv)\n+{\n+\tif (argc <= 1)\n+\t\treturn my_usage();\n+\n+\targc--;\n+\targv++;\n+\n+\tif (argv[0][0] == '-') {\n+\t\tif (!strcmp(argv[0], \"-u\") || !strcmp(argv[0], \"--unit\"))\n+\t\t\treturn unit_tests();\n+\n+\t\tif (!strcmp(argv[0], \"-p\") || !strcmp(argv[0], \"--pretty\")) {\n+\t\t\tpretty = 1;\n+\t\t\targc--;\n+\t\t\targv++;\n+\t\t\tif (argc < 1)\n+\t\t\t\treturn my_usage();\n+\t\t}\n+\t}\n+\n+\treturn scripted(argc, argv);\n+}\ndiff --git a/t/t0019-json-writer.sh b/t/t0019-json-writer.sh\nnew file mode 100755\nindex 0000000..a04c055\n--- /dev/null\n+++ b/t/t0019-json-writer.sh\n@@ -0,0 +1,253 @@\n+#!/bin/sh\n+\n+test_description='test json-writer JSON generation'\n+. ./test-lib.sh\n+\n+test_expect_success 'unit test of json-writer routines' '\n+\ttest-json-writer -u\n+'\n+\n+test_expect_success 'trivial object' '\n+\tcat >expect <<-\\EOF &&\n+\t{}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'trivial array' '\n+\tcat >expect <<-\\EOF &&\n+\t[]\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@array \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'simple object' '\n+\tcat >expect <<-\\EOF &&\n+\t{\"a\":\"abc\",\"b\":42,\"c\":3.14,\"d\":true,\"e\":false,\"f\":null}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-string a abc \\\n+\t\t\t@object-int b 42 \\\n+\t\t\t@object-double c 2 3.140 \\\n+\t\t\t@object-true d \\\n+\t\t\t@object-false e \\\n+\t\t\t@object-null f \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'simple array' '\n+\tcat >expect <<-\\EOF &&\n+\t[\"abc\",42,3.14,true,false,null]\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@array \\\n+\t\t\t@array-string abc \\\n+\t\t\t@array-int 42 \\\n+\t\t\t@array-double 2 3.140 \\\n+\t\t\t@array-true \\\n+\t\t\t@array-false \\\n+\t\t\t@array-null \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'escape quoting string' '\n+\tcat >expect <<-\\EOF &&\n+\t{\"a\":\"abc\\\\def\"}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-string a abc\\\\def \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'escape quoting string 2' '\n+\tcat >expect <<-\\EOF &&\n+\t{\"a\":\"abc\\\"def\"}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-string a abc\\\"def \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'nested inline object' '\n+\tcat >expect <<-\\EOF &&\n+\t{\"a\":\"abc\",\"b\":42,\"sub1\":{\"c\":3.14,\"d\":true,\"sub2\":{\"e\":false,\"f\":null}}}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-string a abc \\\n+\t\t\t@object-int b 42 \\\n+\t\t\t@object-object \"sub1\" \\\n+\t\t\t\t@object-double c 2 3.140 \\\n+\t\t\t\t@object-true d \\\n+\t\t\t\t@object-object \"sub2\" \\\n+\t\t\t\t\t@object-false e \\\n+\t\t\t\t\t@object-null f \\\n+\t\t\t\t@end \\\n+\t\t\t@end \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'nested inline array' '\n+\tcat >expect <<-\\EOF &&\n+\t[\"abc\",42,[3.14,true,[false,null]]]\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@array \\\n+\t\t\t@array-string abc \\\n+\t\t\t@array-int 42 \\\n+\t\t\t@array-array \\\n+\t\t\t\t@array-double 2 3.140 \\\n+\t\t\t\t@array-true \\\n+\t\t\t\t@array-array \\\n+\t\t\t\t\t@array-false \\\n+\t\t\t\t\t@array-null \\\n+\t\t\t\t@end \\\n+\t\t\t@end \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'nested inline object and array' '\n+\tcat >expect <<-\\EOF &&\n+\t{\"a\":\"abc\",\"b\":42,\"sub1\":{\"c\":3.14,\"d\":true,\"sub2\":[false,null]}}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-string a abc \\\n+\t\t\t@object-int b 42 \\\n+\t\t\t@object-object \"sub1\" \\\n+\t\t\t\t@object-double c 2 3.140 \\\n+\t\t\t\t@object-true d \\\n+\t\t\t\t@object-array \"sub2\" \\\n+\t\t\t\t\t@array-false \\\n+\t\t\t\t\t@array-null \\\n+\t\t\t\t@end \\\n+\t\t\t@end \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'nested inline object and array 2' '\n+\tcat >expect <<-\\EOF &&\n+\t{\"a\":\"abc\",\"b\":42,\"sub1\":{\"c\":3.14,\"d\":true,\"sub2\":[false,{\"g\":0,\"h\":1},null]}}\n+\tEOF\n+\ttest-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-string a abc \\\n+\t\t\t@object-int b 42 \\\n+\t\t\t@object-object \"sub1\" \\\n+\t\t\t\t@object-double c 2 3.140 \\\n+\t\t\t\t@object-true d \\\n+\t\t\t\t@object-array \"sub2\" \\\n+\t\t\t\t\t@array-false \\\n+\t\t\t\t\t@array-object \\\n+\t\t\t\t\t\t@object-int g 0 \\\n+\t\t\t\t\t\t@object-int h 1 \\\n+\t\t\t\t\t@end \\\n+\t\t\t\t\t@array-null \\\n+\t\t\t\t@end \\\n+\t\t\t@end \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'pretty nested inline object and array 2' '\n+\tcat >expect <<EOF &&\n+{\n+  \"a\": \"abc\",\n+  \"b\": 42,\n+  \"sub1\": {\n+    \"c\": 3.14,\n+    \"d\": true,\n+    \"sub2\": [\n+      false,\n+      {\n+        \"g\": 0,\n+        \"h\": 1\n+      },\n+      null\n+    ]\n+  }\n+}\n+EOF\n+\ttest-json-writer >actual \\\n+\t\t--pretty \\\n+\t\t@object \\\n+\t\t\t@object-string a abc \\\n+\t\t\t@object-int b 42 \\\n+\t\t\t@object-object \"sub1\" \\\n+\t\t\t\t@object-double c 2 3.140 \\\n+\t\t\t\t@object-true d \\\n+\t\t\t\t@object-array \"sub2\" \\\n+\t\t\t\t\t@array-false \\\n+\t\t\t\t\t@array-object \\\n+\t\t\t\t\t\t@object-int g 0 \\\n+\t\t\t\t\t\t@object-int h 1 \\\n+\t\t\t\t\t@end \\\n+\t\t\t\t\t@array-null \\\n+\t\t\t\t@end \\\n+\t\t\t@end \\\n+\t\t@end &&\n+\ttest_cmp expect actual\n+'\n+\n+test_expect_success 'bogus: array element in object' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@array-string abc \\\n+\t\t@end\n+'\n+\n+test_expect_success 'bogus: object element in array' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@array \\\n+\t\t\t@object-string a abc \\\n+\t\t@end\n+'\n+\n+test_expect_success 'bogus: unterminated child' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-object \"sub1\" \\\n+\t\t\t@end\n+'\n+\n+test_expect_success 'bogus: unterminted top level' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@object\n+'\n+\n+test_expect_success 'bogus: first term' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@object-int a 0\n+'\n+\n+test_expect_success 'bogus: missing val param' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-int a \\\n+\t\t@end\n+'\n+\n+test_expect_success 'bogus: extra token after val param' '\n+\ttest_must_fail test-json-writer >actual \\\n+\t\t@object \\\n+\t\t\t@object-int a 0 1 \\\n+\t\t@end\n+'\n+\n+test_done\n-- \n2.9.3\n\n"},{"id":"343093","messageId":"ce076d5e-f30f-006e-a25e-ff1a900562bf@ramsayjones.plus.com","threadId":"48154","inReplyTo":"20180326143136.47116-2-git@jeffhostetler.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2018-03-27T03:18:21Z","receivedAt":"2018-03-27T03:18:30Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 26/03/18 15:31, git@jeffhostetler.com wrote:\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n> \n> Add a series of jw_ routines and \"struct json_writer\" structure to compose\n> JSON data.  The resulting string data can then be output by commands wanting\n> to support a JSON output format.\n> \n> The json-writer routines can be used to generate structured data in a\n> JSON-like format.  We say \"JSON-like\" because we do not enforce the Unicode\n> (usually UTF-8) requirement on string fields.  Internally, Git does not\n> necessarily have Unicode/UTF-8 data for most fields, so it is currently\n> unclear the best way to enforce that requirement.  For example, on Linx\n> pathnames can contain arbitrary 8-bit character data, so a command like\n> \"status\" would not know how to encode the reported pathnames.  We may want\n> to revisit this (or double encode such strings) in the future.\n> \n> The initial use for the json-writer routines is for generating telemetry\n> data for executed Git commands.  Later, we may want to use them in other\n> commands, such as status.\n> \n> Helped-by: René Scharfe <l.s.r@web.de>\n> Helped-by: Wink Saville <wink@saville.com>\n> Helped-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\n> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n> ---\n>  Makefile                    |   2 +\n>  json-writer.c               | 395 +++++++++++++++++++++++++++++\n>  json-writer.h               |  92 +++++++\n>  t/helper/test-json-writer.c | 590 ++++++++++++++++++++++++++++++++++++++++++++\n>  t/t0019-json-writer.sh      | 253 +++++++++++++++++++\n>  5 files changed, 1332 insertions(+)\n>  create mode 100644 json-writer.c\n>  create mode 100644 json-writer.h\n>  create mode 100644 t/helper/test-json-writer.c\n>  create mode 100755 t/t0019-json-writer.sh\n> \n[snip]\n\nThanks, this version fixes all issues I had (with the compilation\nand sparse warnings).\n\n[Was using UINT64_C(0xffffffffffffffff) a problem on windows?]\n\nATB,\nRamsay Jones\n\n\n"},{"id":"343119","messageId":"5192e88a-41ea-02ed-c765-bfcb96aaba9d@jeffhostetler.com","threadId":"48154","inReplyTo":"ce076d5e-f30f-006e-a25e-ff1a900562bf@ramsayjones.plus.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-27T10:28:22Z","receivedAt":"2018-03-27T10:28:28Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 3/26/2018 11:18 PM, Ramsay Jones wrote:\n> On 26/03/18 15:31, git@jeffhostetler.com wrote:\n>> From: Jeff Hostetler <jeffhost@microsoft.com>\n> [snip]\n> \n> Thanks, this version fixes all issues I had (with the compilation\n> and sparse warnings).\n> \n> [Was using UINT64_C(0xffffffffffffffff) a problem on windows?]\n\nThanks for the confirmation.\n\nI was building on Linux.  I haven't tried using UINT64_C()\nfor anything, but I'll keep that in mind next time.\n\nThanks,\nJeff\n\n\n"},{"id":"343137","messageId":"ad15ccab-088d-b827-a9fa-c83d397c3479@ramsayjones.plus.com","threadId":"48154","inReplyTo":"ce076d5e-f30f-006e-a25e-ff1a900562bf@ramsayjones.plus.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2018-03-27T15:45:17Z","receivedAt":"2018-03-27T15:45:25Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 27/03/18 04:18, Ramsay Jones wrote:\n> On 26/03/18 15:31, git@jeffhostetler.com wrote:\n>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>>\n> [snip]\n> \n> Thanks, this version fixes all issues I had (with the compilation\n> and sparse warnings).\n> \n> [Was using UINT64_C(0xffffffffffffffff) a problem on windows?]\n\nBTW, I forgot to mention that you had some whitespace problems\nwith this patch, viz:\n\n  $ git log --oneline -1 --check master-json\n  ab643d838 (master-json) json_writer: new routines to create data in JSON format\n  t/helper/test-json-writer.c:280: trailing whitespace.\n  + */ \n  t/t0019-json-writer.sh:179: indent with spaces.\n  +        \"g\": 0,\n  t/t0019-json-writer.sh:180: indent with spaces.\n  +        \"h\": 1\n  $ \n\nATB,\nRamsay Jones\n\n"},{"id":"343155","messageId":"2bc6f6a6-bf53-b87c-af02-156ad006a878@jeffhostetler.com","threadId":"48154","inReplyTo":"ad15ccab-088d-b827-a9fa-c83d397c3479@ramsayjones.plus.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-27T17:14:32Z","receivedAt":"2018-03-27T17:14:40Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 3/27/2018 11:45 AM, Ramsay Jones wrote:\n> \n> \n> On 27/03/18 04:18, Ramsay Jones wrote:\n>> On 26/03/18 15:31, git@jeffhostetler.com wrote:\n>>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>>>\n>> [snip]\n>>\n>> Thanks, this version fixes all issues I had (with the compilation\n>> and sparse warnings).\n>>\n>> [Was using UINT64_C(0xffffffffffffffff) a problem on windows?]\n> \n> BTW, I forgot to mention that you had some whitespace problems\n> with this patch, viz:\n\nI ran \"make sparse\" on this and it did not complain about any of this.\n\nWhat command do you run to get these messages?\nI know they appear as red in diffs (and I usually double check that),\nbut I had not seen a command to complain about them like this.\n\n> \n>    $ git log --oneline -1 --check master-json\n>    ab643d838 (master-json) json_writer: new routines to create data in JSON format\n>    t/helper/test-json-writer.c:280: trailing whitespace.\n>    + */\n>    t/t0019-json-writer.sh:179: indent with spaces.\n>    +        \"g\": 0,\n>    t/t0019-json-writer.sh:180: indent with spaces.\n>    +        \"h\": 1\n>    $\n\nthe leading spaces are required in this case.\nthe pretty json output contains 8 spaces for that sub-structure not a tab.\nis there a preferred way to denote this in the test script?\n\nJeff\n\n\n"},{"id":"343158","messageId":"CAKk8isp5NncyDuGNhWctQfmjToWa8nAzHODi7SjbUW2i2R4kiw@mail.gmail.com","threadId":"48154","inReplyTo":"2bc6f6a6-bf53-b87c-af02-156ad006a878@jeffhostetler.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Wink Saville","fromEmail":"wink@saville.com","sentAt":"2018-03-27T17:43:03Z","receivedAt":"2018-03-27T17:43:32Z","isPatch":true,"sender":{"key":"wink@saville.com","avatar":"https://avatars.githubusercontent.com/u/1024284?v=4"},"body":"> the leading spaces are required in this case.\n> the pretty json output contains 8 spaces for that sub-structure not a tab.\n> is there a preferred way to denote this in the test script?\n>\n> Jeff\n\nI've used \"git diff --check\" which I got from Documentation/SubmittingPatches.\n\n-- Wink\n"},{"id":"343159","messageId":"007a01d3c5f5$7fcd3290$7f6797b0$@nexbridge.com","threadId":"48154","inReplyTo":"CAKk8isp5NncyDuGNhWctQfmjToWa8nAzHODi7SjbUW2i2R4kiw@mail.gmail.com","subject":"RE: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Randall S. Becker","fromEmail":"rsbecker@nexbridge.com","sentAt":"2018-03-27T18:00:27Z","receivedAt":"2018-03-27T18:02:54Z","isPatch":true,"sender":{"key":"randall.becker@nexbridge.ca","avatar":"https://avatars.githubusercontent.com/u/28956764?v=4"},"body":"On March 27, 2018 1:43 PM, Wink Saville wrote:\n> > the leading spaces are required in this case.\n> > the pretty json output contains 8 spaces for that sub-structure not a tab.\n> > is there a preferred way to denote this in the test script?\n> >\n> > Jeff\n> \n> I've used \"git diff --check\" which I got from\n> Documentation/SubmittingPatches.\n\nWhile I have not done this in the git suite, my own suites use something along the lines of the following when I need (and have to validate) exact spacing at the beginning of lines in expected output:\n\ncat <<-EOF | sed \"1,\\$s/_/ /g\" >expect\n{ level1 ...\n________{ level2\nEtc.\nEOF\n\nProviding you don't use _ elsewhere in the output. It's a bit hacky but works.\n\nCheers,\nRandall\n\n-- Brief whoami:\n NonStop developer since approximately 211288444200000000\n UNIX developer since approximately 421664400\n-- In my real life, I talk too much.\n\n\n\n\n"},{"id":"343160","messageId":"c9c2ebba-9cde-9eed-bd08-34ce8b543787@ramsayjones.plus.com","threadId":"48154","inReplyTo":"2bc6f6a6-bf53-b87c-af02-156ad006a878@jeffhostetler.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2018-03-27T18:05:33Z","receivedAt":"2018-03-27T18:05:46Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 27/03/18 18:14, Jeff Hostetler wrote:\n> \n> \n> On 3/27/2018 11:45 AM, Ramsay Jones wrote:\n>>\n>>\n>> On 27/03/18 04:18, Ramsay Jones wrote:\n>>> On 26/03/18 15:31, git@jeffhostetler.com wrote:\n>>>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>>>>\n>>> [snip]\n>>>\n>>> Thanks, this version fixes all issues I had (with the compilation\n>>> and sparse warnings).\n>>>\n>>> [Was using UINT64_C(0xffffffffffffffff) a problem on windows?]\n>>\n>> BTW, I forgot to mention that you had some whitespace problems\n>> with this patch, viz:\n> \n> I ran \"make sparse\" on this and it did not complain about any of this.\n\nNo, sparse doesn't check for whitespace issues.\n\n> What command do you run to get these messages?\n\n'git am' told me when I applied the patch from the ML, and ...\n\n> I know they appear as red in diffs (and I usually double check that),\n> but I had not seen a command to complain about them like this.\n\n... since I applied the patch yesterday, it was easier to\ndemonstrate the issue today using this command:\n\n>>    $ git log --oneline -1 --check master-json\n>>    ab643d838 (master-json) json_writer: new routines to create data in JSON format\n>>    t/helper/test-json-writer.c:280: trailing whitespace.\n>>    + */\n>>    t/t0019-json-writer.sh:179: indent with spaces.\n>>    +        \"g\": 0,\n>>    t/t0019-json-writer.sh:180: indent with spaces.\n>>    +        \"h\": 1\n>>    $\n> \n> the leading spaces are required in this case.\n> the pretty json output contains 8 spaces for that sub-structure not a tab.\n> is there a preferred way to denote this in the test script?\n\nOK, I should have looked at the output more closely! :(\n\nOnly the trailing whitespace in test-json-writer.c needs\nto be addressed then.\n\nATB,\nRamsay Jones\n\n\n\n"},{"id":"343199","messageId":"xmqq8tad9epn.fsf@gitster-ct.c.googlers.com","threadId":"48154","inReplyTo":"ad15ccab-088d-b827-a9fa-c83d397c3479@ramsayjones.plus.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-28T01:22:12Z","receivedAt":"2018-03-28T01:22:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ramsay Jones <ramsay@ramsayjones.plus.com> writes:\n\n> BTW, I forgot to mention that you had some whitespace problems\n> with this patch, viz:\n>\n>   $ git log --oneline -1 --check master-json\n>   ab643d838 (master-json) json_writer: new routines to create data in JSON format\n>   t/helper/test-json-writer.c:280: trailing whitespace.\n>   + */ \n>   t/t0019-json-writer.sh:179: indent with spaces.\n>   +        \"g\": 0,\n>   t/t0019-json-writer.sh:180: indent with spaces.\n>   +        \"h\": 1\n>   $ \n\nYes, and the here-doc that shows expected output looked somewhat\nold-fashioned without using <<- indent.\n\nWriting it in a way like this might be a reasonable workaround for\n\"indent with spaces\", which is only about the leading blank used to\nindent the line:\n\n<--HT-->sed -e \"s/^\\|//\" <<-\\EOF\n<--HT-->|  ...\n<--HT-->|        \"g\": 0,\n<--HT-->|        \"h\": 0,\n<--HT-->|  ...\n<--HT-->EOF\n\nThat is, (1) Use the same number of HT as the line that begins the\nhere-doc to indent the expected output; (2) but explicitly mark the\nleft-most column with '|' or something; and then (3) write 8 or more\nspaces liberally as needed to reproduce the expected output.\n\nI called it a \"workaround\", as another possibility is to loosen the\nwhitespace rule in t/.gitattributes; we _could_ declare that indent\nwith spaces is OK in these scripts as they contain many expected\noutput examples in here-doc form.\n\nThe trailing whitespace after closing C-comment is simply a style\nviolation that is not excuable (I think I've dealt with it when I\nqueued the patch); it should just be fixed.\n\nThanks.\n"},{"id":"343207","messageId":"xmqqvadg93l0.fsf@gitster-ct.c.googlers.com","threadId":"48154","inReplyTo":"007a01d3c5f5$7fcd3290$7f6797b0$@nexbridge.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-03-28T05:22:35Z","receivedAt":"2018-03-28T05:22:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Randall S. Becker\" <rsbecker@nexbridge.com> writes:\n\n> cat <<-EOF | sed \"1,\\$s/_/ /g\" >expect\n> { level1 ...\n> ________{ level2\n> Etc.\n> EOF\n>\n> Providing you don't use _ elsewhere in the output. It's a bit hacky but works.\n\nThe trick is especially useful if your golden master has mixture of\nSP and HT, as you can write the source in a way that helps readers\nunderstand what is going on, and turn that with sed into what the\nmachines would want to see.  When used that way, I'd say it is not\n\"bit hacky but works\", but actually is a beautiful solution ;-).\n"},{"id":"349066","messageId":"CACsJy8DcGp29iJDZKUz0JM-bvbE_G3KnqWyLk_efjd7T3URTYw@mail.gmail.com","threadId":"48154","inReplyTo":"20180326143136.47116-2-git@jeffhostetler.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2018-06-02T04:41:06Z","receivedAt":"2018-06-02T04:41:40Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Mar 26, 2018 at 4:31 PM,  <git@jeffhostetler.com> wrote:\n> +static inline void assert_in_array(const struct json_writer *jw)\n> +{\n> +       if (!jw->open_stack.len)\n> +               die(\"json-writer: array: missing jw_array_begin()\");\n\nWhen you reroll, please consider marking all these string for\ntranslation with _(), unless these are for machine consumption.\n\n> +       if (jw->open_stack.buf[jw->open_stack.len - 1] != '[')\n> +               die(\"json-writer: array: not in array\");\n-- \nDuy\n"},{"id":"349070","messageId":"20180602051344.GA28311@sigill.intra.peff.net","threadId":"48154","inReplyTo":"CACsJy8DcGp29iJDZKUz0JM-bvbE_G3KnqWyLk_efjd7T3URTYw@mail.gmail.com","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2018-06-02T05:13:45Z","receivedAt":"2018-06-02T05:19:28Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jun 02, 2018 at 06:41:06AM +0200, Duy Nguyen wrote:\n\n> On Mon, Mar 26, 2018 at 4:31 PM,  <git@jeffhostetler.com> wrote:\n> > +static inline void assert_in_array(const struct json_writer *jw)\n> > +{\n> > +       if (!jw->open_stack.len)\n> > +               die(\"json-writer: array: missing jw_array_begin()\");\n> \n> When you reroll, please consider marking all these string for\n> translation with _(), unless these are for machine consumption.\n\nActually, these are all basically asserts, I think. So ideally they'd be\nBUG()s and not translated.\n\n-Peff\n"},{"id":"349222","messageId":"27b02292-9434-edfb-fa43-549973bf7b93@jeffhostetler.com","threadId":"48154","inReplyTo":"20180602051344.GA28311@sigill.intra.peff.net","subject":"Re: [PATCH v4] json_writer: new routines to create data in JSON format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-06-04T09:41:23Z","receivedAt":"2018-06-04T09:50:30Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 6/2/2018 1:13 AM, Jeff King wrote:\n> On Sat, Jun 02, 2018 at 06:41:06AM +0200, Duy Nguyen wrote:\n> \n>> On Mon, Mar 26, 2018 at 4:31 PM,  <git@jeffhostetler.com> wrote:\n>>> +static inline void assert_in_array(const struct json_writer *jw)\n>>> +{\n>>> +       if (!jw->open_stack.len)\n>>> +               die(\"json-writer: array: missing jw_array_begin()\");\n>>\n>> When you reroll, please consider marking all these string for\n>> translation with _(), unless these are for machine consumption.\n> \n> Actually, these are all basically asserts, I think. So ideally they'd be\n> BUG()s and not translated.\n\nYes, these are basically asserts.  I'll convert them to BUG's and send\nup another version this week.\n\nThanks\nJeff\n\n"}]}