{"thread":{"id":"48126","subject":"[PATCH v3] routines to generate JSON data","startedAt":"2018-03-23T16:30:10Z","lastAt":"2018-03-23T19:36:53Z","messageCount":7,"participants":["git@jeffhostetler.com","Jonathan Nieder","Ramsay Jones","Jeff Hostetler"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"342594","messageId":"20180323162952.81443-1-git@jeffhostetler.com","threadId":"48126","inReplyTo":null,"subject":"[PATCH v3] routines to generate JSON data","fromName":"","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-23T16:29:51Z","receivedAt":"2018-03-23T16:30:10Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nThis is version 3 of my JSON data format routines.\n\nThis version addresses the variable name changes in [v2] and adds additional\ntest cases.  I also changed the BUG() calls to die() to help with testing.\n\nThe json-writer routines can be used generate structured data in a JSON-like\nformat.  I say \"JSON-like\" because we don't enforce the Unicode/UTF-8\nrequirement [3,4] on string values.  This was discussed on the mailing list\nin the [v1] and [v2] threads, but to summarize here: Git doesn't know if\nvarious fields, such as Unix pathnames and author names, are Unicode or just\n8-bit character data, so Git would not know how to properly encode such\nfields and the consumer of such output would not know these strings were\nencoded (once or twice).  So, until we have a pressing need to generate\nproper Unicode data, we avoid it for now.\n\nThe initial use for the json-writer routines is for generating telemetry data\nfor executed Git commands.  Later, we might want to use them in other commands\nsuch as status.\n\n[v1] https://public-inbox.org/git/20180316194057.77513-1-git@jeffhostetler.com/\n[v2] https://public-inbox.org/git/20180321192827.44330-1-git@jeffhostetler.com/\n[3]  http://www.ietf.org/rfc/rfc7159.txt\n[4]  http://json.org/\n\n\nJeff Hostetler (1):\n  json_writer: new routines to create data in JSON format\n\n Makefile                    |   2 +\n json-writer.c               | 321 +++++++++++++++++++++++++++++++++\n json-writer.h               |  86 +++++++++\n t/helper/test-json-writer.c | 420 ++++++++++++++++++++++++++++++++++++++++++++\n t/t0019-json-writer.sh      | 213 ++++++++++++++++++++++\n 5 files changed, 1042 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":"342595","messageId":"20180323162952.81443-2-git@jeffhostetler.com","threadId":"48126","inReplyTo":"20180323162952.81443-1-git@jeffhostetler.com","subject":"[PATCH v3] json_writer: new routines to create data in JSON format","fromName":"","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-23T16:29:52Z","receivedAt":"2018-03-23T16:30:13Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nAdd basic routines to generate data in JSON format.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n---\n Makefile                    |   2 +\n json-writer.c               | 321 +++++++++++++++++++++++++++++++++\n json-writer.h               |  86 +++++++++\n t/helper/test-json-writer.c | 420 ++++++++++++++++++++++++++++++++++++++++++++\n t/t0019-json-writer.sh      | 213 ++++++++++++++++++++++\n 5 files changed, 1042 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..1861382\n--- /dev/null\n+++ b/json-writer.c\n@@ -0,0 +1,321 @@\n+#include \"cache.h\"\n+#include \"json-writer.h\"\n+\n+static char ch_open[2]  = { '{', '[' };\n+static char ch_close[2] = { '}', ']' };\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+\tstrbuf_addch(out, '\"');\n+\tfor (/**/; *in; in++) {\n+\t\tunsigned char c = (unsigned char)*in;\n+\t\tif (c == '\"')\n+\t\t\tstrbuf_add(out, \"\\\\\\\"\", 2);\n+\t\telse if (c == '\\\\')\n+\t\t\tstrbuf_add(out, \"\\\\\\\\\", 2);\n+\t\telse if (c == '\\n')\n+\t\t\tstrbuf_add(out, \"\\\\n\", 2);\n+\t\telse if (c == '\\r')\n+\t\t\tstrbuf_add(out, \"\\\\r\", 2);\n+\t\telse if (c == '\\t')\n+\t\t\tstrbuf_add(out, \"\\\\t\", 2);\n+\t\telse if (c == '\\f')\n+\t\t\tstrbuf_add(out, \"\\\\f\", 2);\n+\t\telse if (c == '\\b')\n+\t\t\tstrbuf_add(out, \"\\\\b\", 2);\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+\n+static inline void begin(struct json_writer *jw, int is_array)\n+{\n+\tALLOC_GROW(jw->level, jw->nr + 1, jw->alloc);\n+\n+\tjw->level[jw->nr].is_array = !!is_array;\n+\tjw->level[jw->nr].is_empty = 1;\n+\n+\tstrbuf_addch(&jw->json, ch_open[!!is_array]);\n+\n+\tjw->nr++;\n+}\n+\n+/*\n+ * Assert that we have an open object at this level.\n+ */\n+static void inline assert_in_object(const struct json_writer *jw, const char *key)\n+{\n+\tif (!jw->nr)\n+\t\tdie(\"json-writer: object: missing jw_object_begin(): '%s'\", key);\n+\tif (jw->level[jw->nr - 1].is_array)\n+\t\tdie(\"json-writer: object: not in object: '%s'\", key);\n+}\n+\n+/*\n+ * Assert that we have an open array at this level.\n+ */\n+static void inline assert_in_array(const struct json_writer *jw)\n+{\n+\tif (!jw->nr)\n+\t\tdie(\"json-writer: array: missing jw_begin()\");\n+\tif (!jw->level[jw->nr - 1].is_array)\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 void inline maybe_add_comma(struct json_writer *jw)\n+{\n+\tif (jw->level[jw->nr - 1].is_empty)\n+\t\tjw->level[jw->nr - 1].is_empty = 0;\n+\telse\n+\t\tstrbuf_addch(&jw->json, ',');\n+}\n+\n+/*\n+ * Assert that the given JSON object or JSON array has been properly\n+ * terminated.  (Has closing bracket.)\n+ */\n+static void inline assert_is_terminated(const struct json_writer *jw)\n+{\n+\tif (jw->nr)\n+\t\tdie(\"json-writer: object: missing jw_end(): '%s'\", jw->json.buf);\n+}\n+\n+void jw_object_begin(struct json_writer *jw)\n+{\n+\tbegin(jw, 0);\n+}\n+\n+void jw_object_string(struct json_writer *jw, const char *key, const char *value)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addch(&jw->json, ':');\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+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, 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+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addf(&jw->json, \":%\"PRIuMAX, value);\n+}\n+\n+void jw_object_double(struct json_writer *jw, const char *fmt,\n+\t\t      const char *key, double value)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tif (!fmt || !*fmt)\n+\t\tfmt = \"%f\";\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addch(&jw->json, ':');\n+\tstrbuf_addf(&jw->json, fmt, value);\n+}\n+\n+void jw_object_true(struct json_writer *jw, const char *key)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addstr(&jw->json, \":true\");\n+}\n+\n+void jw_object_false(struct json_writer *jw, const char *key)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addstr(&jw->json, \":false\");\n+}\n+\n+void jw_object_null(struct json_writer *jw, const char *key)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addstr(&jw->json, \":null\");\n+}\n+\n+void jw_object_sub(struct json_writer *jw, const char *key,\n+\t\t   const struct json_writer *value)\n+{\n+\tassert_is_terminated(value);\n+\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addch(&jw->json, ':');\n+\tstrbuf_addstr(&jw->json, value->json.buf);\n+}\n+\n+void jw_object_inline_begin_object(struct json_writer *jw, const char *key)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addch(&jw->json, ':');\n+\n+\tjw_object_begin(jw);\n+}\n+\n+void jw_object_inline_begin_array(struct json_writer *jw, const char *key)\n+{\n+\tassert_in_object(jw, key);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, key);\n+\tstrbuf_addch(&jw->json, ':');\n+\n+\tjw_array_begin(jw);\n+}\n+\n+void jw_array_begin(struct json_writer *jw)\n+{\n+\tbegin(jw, 1);\n+}\n+\n+void jw_array_string(struct json_writer *jw, const char *value)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tappend_quoted_string(&jw->json, value);\n+}\n+\n+void jw_array_int(struct json_writer *jw,int value)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tstrbuf_addf(&jw->json, \"%d\", value);\n+}\n+\n+void jw_array_uint64(struct json_writer *jw, uint64_t value)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tstrbuf_addf(&jw->json, \"%\"PRIuMAX, value);\n+}\n+\n+void jw_array_double(struct json_writer *jw, const char *fmt, double value)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tif (!fmt || !*fmt)\n+\t\tfmt = \"%f\";\n+\n+\tstrbuf_addf(&jw->json, fmt, value);\n+}\n+\n+void jw_array_true(struct json_writer *jw)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tstrbuf_addstr(&jw->json, \"true\");\n+}\n+\n+void jw_array_false(struct json_writer *jw)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tstrbuf_addstr(&jw->json, \"false\");\n+}\n+\n+void jw_array_null(struct json_writer *jw)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tstrbuf_addstr(&jw->json, \"null\");\n+}\n+\n+void jw_array_sub(struct json_writer *jw, const struct json_writer *value)\n+{\n+\tassert_is_terminated(value);\n+\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tstrbuf_addstr(&jw->json, value->json.buf);\n+}\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+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tjw_object_begin(jw);\n+}\n+\n+void jw_array_inline_begin_array(struct json_writer *jw)\n+{\n+\tassert_in_array(jw);\n+\tmaybe_add_comma(jw);\n+\n+\tjw_array_begin(jw);\n+}\n+\n+int jw_is_terminated(const struct json_writer *jw)\n+{\n+\treturn !jw->nr;\n+}\n+\n+void jw_end(struct json_writer *jw)\n+{\n+\tif (!jw->nr)\n+\t\tdie(\"json-writer: too many jw_end(): '%s'\", jw->json.buf);\n+\n+\tjw->nr--;\n+\n+\tstrbuf_addch(&jw->json,\n+\t\t     ch_close[jw->level[jw->nr].is_array]);\n+}\ndiff --git a/json-writer.h b/json-writer.h\nnew file mode 100644\nindex 0000000..a48ab95\n--- /dev/null\n+++ b/json-writer.h\n@@ -0,0 +1,86 @@\n+#ifndef JSON_WRITER_H\n+#define JSON_WRITER_H\n+\n+/*\n+ * JSON data structures are defined at:\n+ *      http://json.org/\n+ *      http://www.ietf.org/rfc/rfc7159.txt\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+\n+struct json_writer_level\n+{\n+\tunsigned is_array : 1;\n+\tunsigned is_empty : 1;\n+};\n+\n+struct json_writer\n+{\n+\tstruct json_writer_level *level;\n+\tint nr, alloc;\n+\tstruct strbuf json;\n+};\n+\n+#define JSON_WRITER_INIT { NULL, 0, 0, STRBUF_INIT }\n+\n+void jw_object_begin(struct json_writer *jw);\n+void jw_array_begin(struct json_writer *jw);\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 *fmt,\n+\t\t      const char *key, 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(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+\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, const char *fmt, 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(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..4bf815c\n--- /dev/null\n+++ b/t/helper/test-json-writer.c\n@@ -0,0 +1,420 @@\n+#include \"cache.h\"\n+#include \"json-writer.h\"\n+\n+const char *expect_obj1 = \"{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true}\";\n+const char *expect_obj2 = \"{\\\"a\\\":-1,\\\"b\\\":2147483647,\\\"c\\\":0}\";\n+const char *expect_obj3 = \"{\\\"a\\\":0,\\\"b\\\":4294967295,\\\"c\\\":18446744073709551615}\";\n+const char *expect_obj4 = \"{\\\"t\\\":true,\\\"f\\\":false,\\\"n\\\":null}\";\n+const char *expect_obj5 = \"{\\\"abc\\\\tdef\\\":\\\"abc\\\\\\\\def\\\"}\";\n+\n+struct json_writer obj1 = JSON_WRITER_INIT;\n+struct json_writer obj2 = JSON_WRITER_INIT;\n+struct json_writer obj3 = JSON_WRITER_INIT;\n+struct json_writer obj4 = JSON_WRITER_INIT;\n+struct json_writer obj5 = JSON_WRITER_INIT;\n+\n+\n+void make_obj1(void)\n+{\n+\tjw_object_begin(&obj1);\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+void make_obj2(void)\n+{\n+\tjw_object_begin(&obj2);\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+void make_obj3(void)\n+{\n+\tjw_object_begin(&obj3);\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\", 0xffffffffffffffff);\n+\t}\n+\tjw_end(&obj3);\n+}\n+\n+void make_obj4(void)\n+{\n+\tjw_object_begin(&obj4);\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+void make_obj5(void)\n+{\n+\tjw_object_begin(&obj5);\n+\t{\n+\t\tjw_object_string(&obj5, \"abc\" \"\\x09\" \"def\", \"abc\" \"\\\\\" \"def\");\n+\t}\n+\tjw_end(&obj5);\n+}\n+\n+const char *expect_arr1 = \"[\\\"abc\\\",42,true]\";\n+const char *expect_arr2 = \"[-1,2147483647,0]\";\n+const char *expect_arr3 = \"[0,4294967295,18446744073709551615]\";\n+const char *expect_arr4 = \"[true,false,null]\";\n+\n+struct json_writer arr1 = JSON_WRITER_INIT;\n+struct json_writer arr2 = JSON_WRITER_INIT;\n+struct json_writer arr3 = JSON_WRITER_INIT;\n+struct json_writer arr4 = JSON_WRITER_INIT;\n+\n+void make_arr1(void)\n+{\n+\tjw_array_begin(&arr1);\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+void make_arr2(void)\n+{\n+\tjw_array_begin(&arr2);\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+void make_arr3(void)\n+{\n+\tjw_array_begin(&arr3);\n+\t{\n+\t\tjw_array_uint64(&arr3, 0);\n+\t\tjw_array_uint64(&arr3, 0xffffffff);\n+\t\tjw_array_uint64(&arr3, 0xffffffffffffffff);\n+\t}\n+\tjw_end(&arr3);\n+}\n+\n+void make_arr4(void)\n+{\n+\tjw_array_begin(&arr4);\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+char *expect_nest1 =\n+\t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n+\n+struct json_writer nest1 = JSON_WRITER_INIT;\n+\n+void make_nest1(void)\n+{\n+\tjw_object_begin(&nest1);\n+\t{\n+\t\tjw_object_sub(&nest1, \"obj1\", &obj1);\n+\t\tjw_object_sub(&nest1, \"arr1\", &arr1);\n+\t}\n+\tjw_end(&nest1);\n+}\n+\n+char *expect_inline1 =\n+\t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n+struct json_writer inline1 = JSON_WRITER_INIT;\n+\n+\n+void make_inline1(void)\n+{\n+\tjw_object_begin(&inline1);\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+char *expect_inline2 =\n+\t\"[[1,2],[3,4],{\\\"a\\\":\\\"abc\\\"}]\";\n+struct json_writer inline2 = JSON_WRITER_INIT;\n+\n+void make_inline2(void)\n+{\n+\tjw_array_begin(&inline2);\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+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(); cmp(#v, &v, expect_##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+int unit_tests(void)\n+{\n+\tt(obj1);\n+\tt(obj2);\n+\tt(obj3);\n+\tt(obj4);\n+\tt(obj5);\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+\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 FMT() \\\n+\tSTMT( fmt = argv[++k]; \\\n+\t      PARAM(a_k, \"fmt\", fmt); )\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 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 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);\n+\telse if (!strcmp(argv[0], \"@array\"))\n+\t\tjw_array_begin(&jw);\n+\telse\n+\t\tdie(\"first script term must be '@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\tconst char *fmt;\n+\t\tchar *endptr;\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\tFMT();\n+\t\t\tKEY();\n+\t\t\tVAL_DOUBLE();\n+\t\t\tjw_object_double(&jw, fmt, key, 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\tFMT();\n+\t\t\tVAL_DOUBLE();\n+\t\t\tjw_array_double(&jw, fmt, 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' | '@object ... @end' | '@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+\tif (argv[1][0] == '-') {\n+\t\tif (!strcmp(argv[1], \"-u\"))\n+\t\t\treturn unit_tests();\n+\n+\t\treturn my_usage();\n+\t}\n+\n+\treturn scripted(argc - 1, argv + 1);\n+}\ndiff --git a/t/t0019-json-writer.sh b/t/t0019-json-writer.sh\nnew file mode 100755\nindex 0000000..5674766\n--- /dev/null\n+++ b/t/t0019-json-writer.sh\n@@ -0,0 +1,213 @@\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 %.2f c 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 %.2f 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 %.2f c 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 %.2f 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 'mixed 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 %.2f c 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 'mixed 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 %.2f c 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":"342596","messageId":"20180323164026.GC179915@aiede.svl.corp.google.com","threadId":"48126","inReplyTo":"20180323162952.81443-1-git@jeffhostetler.com","subject":"Re: [PATCH v3] routines to generate JSON data","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-03-23T16:41:00Z","receivedAt":"2018-03-23T16:41:28Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\ngit@jeffhostetler.com wrote:\n\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n>\n> This is version 3 of my JSON data format routines.\n>\n> This version addresses the variable name changes in [v2] and adds additional\n> test cases.  I also changed the BUG() calls to die() to help with testing.\n\nCan the information below go in the commit message?\n\nUsually to avoid that kind of problem, I don't send a cover letter in\none-patch series.  Information that I don't want to make part of\npermanent history (like the above) can go after the three-dash line to\nensure it doesn't go in the commit message.\n\nThanks,\nJonathan\n\n> The json-writer routines can be used generate structured data in a JSON-like\n> format.  I say \"JSON-like\" because we don't enforce the Unicode/UTF-8\n> requirement [3,4] on string values.  This was discussed on the mailing list\n> in the [v1] and [v2] threads, but to summarize here: Git doesn't know if\n> various fields, such as Unix pathnames and author names, are Unicode or just\n> 8-bit character data, so Git would not know how to properly encode such\n> fields and the consumer of such output would not know these strings were\n> encoded (once or twice).  So, until we have a pressing need to generate\n> proper Unicode data, we avoid it for now.\n>\n> The initial use for the json-writer routines is for generating telemetry data\n> for executed Git commands.  Later, we might want to use them in other commands\n> such as status.\n>\n> [v1] https://public-inbox.org/git/20180316194057.77513-1-git@jeffhostetler.com/\n> [v2] https://public-inbox.org/git/20180321192827.44330-1-git@jeffhostetler.com/\n> [3]  http://www.ietf.org/rfc/rfc7159.txt\n> [4]  http://json.org/\n"},{"id":"342606","messageId":"20180323171828.GD179915@aiede.svl.corp.google.com","threadId":"48126","inReplyTo":"20180323162952.81443-2-git@jeffhostetler.com","subject":"Re: [PATCH v3] json_writer: new routines to create data in JSON format","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2018-03-23T17:18:28Z","receivedAt":"2018-03-23T17:18:42Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"git@jeffhostetler.com wrote:\n\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n>\n> Add basic routines to generate data in JSON format.\n>\n> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n\nIf I understand the cover letter correctly, this is a JSON-like\nformat, not actual JSON.  Am I understanding correctly?\n\nWhat are the requirements for consuming this output?  Will a typical\nJSON library be able to handle it without trouble?  If not, is there\nsome subset of cases where a typical JSON library is able to handle it\nwithout trouble?\n\nCan you say a little about the motivation here?  (I know you already\nput some of that in the cover letter, but since that doesn't become\npart of permanent history, it doesn't help the audience that matters.)\n\nThis would also be easier to review if there were an application of it\nin the same series.  It's fine to send an RFC like this without such\nan application, but I think we should hold off on merging it until we\nhave one.  Having an application makes review much easier since we can\nsee how the API works in practice, how well the approach fits what\nusers need, etc.\n\nThanks and hope that helps,\nJonathan\n"},{"id":"342657","messageId":"9234314d-6fb5-13b3-919f-f5a36c6ab688@ramsayjones.plus.com","threadId":"48126","inReplyTo":"20180323162952.81443-1-git@jeffhostetler.com","subject":"Re: [PATCH v3] routines to generate JSON data","fromName":"Ramsay Jones","fromEmail":"ramsay@ramsayjones.plus.com","sentAt":"2018-03-23T18:14:02Z","receivedAt":"2018-03-23T18:14:13Z","isPatch":true,"sender":{"key":"ramsay@ramsayjones.plus.com","avatar":"https://avatars.githubusercontent.com/u/33702710?v=4"},"body":"\n\nOn 23/03/18 16:29, git@jeffhostetler.com wrote:\n> From: Jeff Hostetler <jeffhost@microsoft.com>\n> \n> This is version 3 of my JSON data format routines.\n\nI have not looked at v3 yet - the patch below is against\nthe version in 'pu' @3284f940c (presumably that would be v2).\n\nThe version in 'pu' broke my build on Linux, but not on\ncygwin - which was a bit of a surprise. That is, until I\nsaw the warnings and remembered that I have this in my\nconfig.mak on Linux, but not on cygwin:\n\n  ifneq ($(CC),clang)\n  CFLAGS += -Wold-style-declaration\n  CFLAGS += -Wno-pointer-to-int-cast\n  CFLAGS += -Wsystem-headers\n  endif\n\n... and the warnings were:\n\n  $ diff nout pout\n  1c1\n  < GIT_VERSION = 2.17.0.rc1.317.g4a561d2cc\n  ---\n  > GIT_VERSION = 2.17.0.rc1.445.g3284f940c\n  29a30\n  >     CC commit-graph.o\n  73a75,87\n  >     CC json-writer.o\n  > json-writer.c:53:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n  >  static void inline assert_in_object(const struct json_writer *jw, const char *key)\n  >  ^\n  > json-writer.c:64:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n  >  static void inline assert_in_array(const struct json_writer *jw)\n  >  ^\n  > json-writer.c:75:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n  >  static void inline maybe_add_comma(struct json_writer *jw)\n  >  ^\n  > json-writer.c:87:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n  >  static void inline assert_is_terminated(const struct json_writer *jw)\n  >  ^\n  83a98\n  >     CC ls-refs.o\n\n  ...\n  $ \n\nThe '-Wold-style-declaration' gcc warning flag is not a standard\nproject flag, and I can't quite remember why I have it set, so I\nguess you could just ignore it. However, all other 'static inline'\nfunctions in the project have the inline keyword before the return\ntype, so ... ;-)\n\nAlso, sparse spewed 40 warnings for t/helper/test-json-writer.c,\nwhich were mainly about file-local symbols, but had a couple of\n'constant is so large ...', like so:\n\n  $ grep warning psp-out | head -8\n  t/helper/test-json-writer.c:45:46: warning: constant 0xffffffffffffffff is so big it is unsigned long\n  t/helper/test-json-writer.c:108:40: warning: constant 0xffffffffffffffff is so big it is unsigned long\n  t/helper/test-json-writer.c:4:12: warning: symbol 'expect_obj1' was not declared. Should it be static?\n  t/helper/test-json-writer.c:5:12: warning: symbol 'expect_obj2' was not declared. Should it be static?\n  t/helper/test-json-writer.c:6:12: warning: symbol 'expect_obj3' was not declared. Should it be static?\n  t/helper/test-json-writer.c:7:12: warning: symbol 'expect_obj4' was not declared. Should it be static?\n  t/helper/test-json-writer.c:8:12: warning: symbol 'expect_obj5' was not declared. Should it be static?\n  t/helper/test-json-writer.c:10:20: warning: symbol 'obj1' was not declared. Should it be static?\n  $ \n\nI decided to use the UINT64_C(v) macro from <stdint.h>, which is\na C99 feature, and will (hopefully) not be a problem.\n\nATB,\nRamsay Jones\n\n-- >8 --\nSubject: [PATCH] json-writer: fix up gcc and sparse warnings\n\nSigned-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\n---\n json-writer.c               |  8 ++---\n t/helper/test-json-writer.c | 80 ++++++++++++++++++++++-----------------------\n 2 files changed, 44 insertions(+), 44 deletions(-)\n\ndiff --git a/json-writer.c b/json-writer.c\nindex 89a6abb57..ba0365d20 100644\n--- a/json-writer.c\n+++ b/json-writer.c\n@@ -50,7 +50,7 @@ static inline void begin(struct json_writer *jw, int is_array)\n /*\n  * Assert that we have an open object at this level.\n  */\n-static void inline assert_in_object(const struct json_writer *jw, const char *key)\n+static inline void assert_in_object(const struct json_writer *jw, const char *key)\n {\n \tif (!jw->nr)\n \t\tBUG(\"object: missing jw_object_begin(): '%s'\", key);\n@@ -61,7 +61,7 @@ static void inline assert_in_object(const struct json_writer *jw, const char *ke\n /*\n  * Assert that we have an open array at this level.\n  */\n-static void inline assert_in_array(const struct json_writer *jw)\n+static inline void assert_in_array(const struct json_writer *jw)\n {\n \tif (!jw->nr)\n \t\tBUG(\"array: missing jw_begin()\");\n@@ -72,7 +72,7 @@ static void inline assert_in_array(const struct json_writer *jw)\n /*\n  * Add comma if we have already seen a member at this level.\n  */\n-static void inline maybe_add_comma(struct json_writer *jw)\n+static inline void maybe_add_comma(struct json_writer *jw)\n {\n \tif (jw->levels[jw->nr - 1].level_is_empty)\n \t\tjw->levels[jw->nr - 1].level_is_empty = 0;\n@@ -84,7 +84,7 @@ static void inline maybe_add_comma(struct json_writer *jw)\n  * Assert that the given JSON object or JSON array has been properly\n  * terminated.  (Has closing bracket.)\n  */\n-static void inline assert_is_terminated(const struct json_writer *jw)\n+static inline void assert_is_terminated(const struct json_writer *jw)\n {\n \tif (jw->nr)\n \t\tBUG(\"object: missing jw_end(): '%s'\", jw->json.buf);\ndiff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c\nindex 4bf815c2d..9218ee9df 100644\n--- a/t/helper/test-json-writer.c\n+++ b/t/helper/test-json-writer.c\n@@ -1,20 +1,20 @@\n #include \"cache.h\"\n #include \"json-writer.h\"\n \n-const char *expect_obj1 = \"{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true}\";\n-const char *expect_obj2 = \"{\\\"a\\\":-1,\\\"b\\\":2147483647,\\\"c\\\":0}\";\n-const char *expect_obj3 = \"{\\\"a\\\":0,\\\"b\\\":4294967295,\\\"c\\\":18446744073709551615}\";\n-const char *expect_obj4 = \"{\\\"t\\\":true,\\\"f\\\":false,\\\"n\\\":null}\";\n-const char *expect_obj5 = \"{\\\"abc\\\\tdef\\\":\\\"abc\\\\\\\\def\\\"}\";\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 \n-struct json_writer obj1 = JSON_WRITER_INIT;\n-struct json_writer obj2 = JSON_WRITER_INIT;\n-struct json_writer obj3 = JSON_WRITER_INIT;\n-struct json_writer obj4 = JSON_WRITER_INIT;\n-struct json_writer obj5 = JSON_WRITER_INIT;\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 \n \n-void make_obj1(void)\n+static void make_obj1(void)\n {\n \tjw_object_begin(&obj1);\n \t{\n@@ -25,7 +25,7 @@ void make_obj1(void)\n \tjw_end(&obj1);\n }\n \n-void make_obj2(void)\n+static void make_obj2(void)\n {\n \tjw_object_begin(&obj2);\n \t{\n@@ -36,18 +36,18 @@ void make_obj2(void)\n \tjw_end(&obj2);\n }\n \n-void make_obj3(void)\n+static void make_obj3(void)\n {\n \tjw_object_begin(&obj3);\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\", 0xffffffffffffffff);\n+\t\tjw_object_uint64(&obj3, \"c\", UINT64_C(0xffffffffffffffff));\n \t}\n \tjw_end(&obj3);\n }\n \n-void make_obj4(void)\n+static void make_obj4(void)\n {\n \tjw_object_begin(&obj4);\n \t{\n@@ -58,7 +58,7 @@ void make_obj4(void)\n \tjw_end(&obj4);\n }\n \n-void make_obj5(void)\n+static void make_obj5(void)\n {\n \tjw_object_begin(&obj5);\n \t{\n@@ -67,17 +67,17 @@ void make_obj5(void)\n \tjw_end(&obj5);\n }\n \n-const char *expect_arr1 = \"[\\\"abc\\\",42,true]\";\n-const char *expect_arr2 = \"[-1,2147483647,0]\";\n-const char *expect_arr3 = \"[0,4294967295,18446744073709551615]\";\n-const char *expect_arr4 = \"[true,false,null]\";\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-struct json_writer arr1 = JSON_WRITER_INIT;\n-struct json_writer arr2 = JSON_WRITER_INIT;\n-struct json_writer arr3 = JSON_WRITER_INIT;\n-struct json_writer arr4 = JSON_WRITER_INIT;\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-void make_arr1(void)\n+static void make_arr1(void)\n {\n \tjw_array_begin(&arr1);\n \t{\n@@ -88,7 +88,7 @@ void make_arr1(void)\n \tjw_end(&arr1);\n }\n \n-void make_arr2(void)\n+static void make_arr2(void)\n {\n \tjw_array_begin(&arr2);\n \t{\n@@ -99,18 +99,18 @@ void make_arr2(void)\n \tjw_end(&arr2);\n }\n \n-void make_arr3(void)\n+static void make_arr3(void)\n {\n \tjw_array_begin(&arr3);\n \t{\n \t\tjw_array_uint64(&arr3, 0);\n \t\tjw_array_uint64(&arr3, 0xffffffff);\n-\t\tjw_array_uint64(&arr3, 0xffffffffffffffff);\n+\t\tjw_array_uint64(&arr3, UINT64_C(0xffffffffffffffff));\n \t}\n \tjw_end(&arr3);\n }\n \n-void make_arr4(void)\n+static void make_arr4(void)\n {\n \tjw_array_begin(&arr4);\n \t{\n@@ -121,12 +121,12 @@ void make_arr4(void)\n \tjw_end(&arr4);\n }\n \n-char *expect_nest1 =\n+static char *expect_nest1 =\n \t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n \n-struct json_writer nest1 = JSON_WRITER_INIT;\n+static struct json_writer nest1 = JSON_WRITER_INIT;\n \n-void make_nest1(void)\n+static void make_nest1(void)\n {\n \tjw_object_begin(&nest1);\n \t{\n@@ -136,12 +136,12 @@ void make_nest1(void)\n \tjw_end(&nest1);\n }\n \n-char *expect_inline1 =\n+static char *expect_inline1 =\n \t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n-struct json_writer inline1 = JSON_WRITER_INIT;\n+static struct json_writer inline1 = JSON_WRITER_INIT;\n \n \n-void make_inline1(void)\n+static void make_inline1(void)\n {\n \tjw_object_begin(&inline1);\n \t{\n@@ -163,11 +163,11 @@ void make_inline1(void)\n \tjw_end(&inline1);\n }\n \n-char *expect_inline2 =\n+static char *expect_inline2 =\n \t\"[[1,2],[3,4],{\\\"a\\\":\\\"abc\\\"}]\";\n-struct json_writer inline2 = JSON_WRITER_INIT;\n+static struct json_writer inline2 = JSON_WRITER_INIT;\n \n-void make_inline2(void)\n+static void make_inline2(void)\n {\n \tjw_array_begin(&inline2);\n \t{\n@@ -193,7 +193,7 @@ void make_inline2(void)\n }\n \n \n-void cmp(const char *test, const struct json_writer *jw, const char *exp)\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@@ -209,7 +209,7 @@ void cmp(const char *test, const struct json_writer *jw, const char *exp)\n  * Run some basic regression tests with some known patterns.\n  * These tests also demonstrate how to use the jw_ API.\n  */\n-int unit_tests(void)\n+static int unit_tests(void)\n {\n \tt(obj1);\n \tt(obj2);\n-- \n2.16.0\n\n"},{"id":"342668","messageId":"6b27b09b-727d-1c3f-c311-5cf6c9bafb4f@jeffhostetler.com","threadId":"48126","inReplyTo":"20180323171828.GD179915@aiede.svl.corp.google.com","subject":"Re: [PATCH v3] json_writer: new routines to create data in JSON format","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-23T19:33:46Z","receivedAt":"2018-03-23T19:33:54Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 3/23/2018 1:18 PM, Jonathan Nieder wrote:\n> git@jeffhostetler.com wrote:\n> \n>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>>\n>> Add basic routines to generate data in JSON format.\n>>\n>> Signed-off-by: Jeff Hostetler <jeffhost@microsoft.com>\n> \n> If I understand the cover letter correctly, this is a JSON-like\n> format, not actual JSON.  Am I understanding correctly?\n> \n> What are the requirements for consuming this output?  Will a typical\n> JSON library be able to handle it without trouble?  If not, is there\n> some subset of cases where a typical JSON library is able to handle it\n> without trouble?\n\nI'll add text to the commit message and the .h file explaining\nthe Unicode limitation in the next reroll.\n\n> \n> Can you say a little about the motivation here?  (I know you already\n> put some of that in the cover letter, but since that doesn't become\n> part of permanent history, it doesn't help the audience that matters.)\n\nI'll add a note about that to the commit message.\nThanks.\n\n> \n> This would also be easier to review if there were an application of it\n> in the same series.  It's fine to send an RFC like this without such\n> an application, but I think we should hold off on merging it until we\n> have one.  Having an application makes review much easier since we can\n> see how the API works in practice, how well the approach fits what\n> users need, etc.\n\nThat's fine.  I'm planning to push up another patch series next week\nthat builds upon this, so hopefully that will help.\n\n> \n> Thanks and hope that helps,\n> Jonathan\n\nThanks,\nJeff\n\n"},{"id":"342670","messageId":"49fbcdd2-c486-7660-1304-736607352c9c@jeffhostetler.com","threadId":"48126","inReplyTo":"9234314d-6fb5-13b3-919f-f5a36c6ab688@ramsayjones.plus.com","subject":"Re: [PATCH v3] routines to generate JSON data","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2018-03-23T19:36:47Z","receivedAt":"2018-03-23T19:36:53Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 3/23/2018 2:14 PM, Ramsay Jones wrote:\n> \n> \n> On 23/03/18 16:29, git@jeffhostetler.com wrote:\n>> From: Jeff Hostetler <jeffhost@microsoft.com>\n>>\n>> This is version 3 of my JSON data format routines.\n> \n> I have not looked at v3 yet - the patch below is against\n> the version in 'pu' @3284f940c (presumably that would be v2).\n> \n\nI built my v3 on Linux and didn't see any warnings.  I'll add\nyour extra CFLAGS and fix anything that I find and send up a v4\nshortly.\n\nThanks.\nJeff\n\n> The version in 'pu' broke my build on Linux, but not on\n> cygwin - which was a bit of a surprise. That is, until I\n> saw the warnings and remembered that I have this in my\n> config.mak on Linux, but not on cygwin:\n> \n>    ifneq ($(CC),clang)\n>    CFLAGS += -Wold-style-declaration\n>    CFLAGS += -Wno-pointer-to-int-cast\n>    CFLAGS += -Wsystem-headers\n>    endif\n> \n> ... and the warnings were:\n> \n>    $ diff nout pout\n>    1c1\n>    < GIT_VERSION = 2.17.0.rc1.317.g4a561d2cc\n>    ---\n>    > GIT_VERSION = 2.17.0.rc1.445.g3284f940c\n>    29a30\n>    >     CC commit-graph.o\n>    73a75,87\n>    >     CC json-writer.o\n>    > json-writer.c:53:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n>    >  static void inline assert_in_object(const struct json_writer *jw, const char *key)\n>    >  ^\n>    > json-writer.c:64:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n>    >  static void inline assert_in_array(const struct json_writer *jw)\n>    >  ^\n>    > json-writer.c:75:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n>    >  static void inline maybe_add_comma(struct json_writer *jw)\n>    >  ^\n>    > json-writer.c:87:1: warning: ‘inline’ is not at beginning of declaration [-Wold-style-declaration]\n>    >  static void inline assert_is_terminated(const struct json_writer *jw)\n>    >  ^\n>    83a98\n>    >     CC ls-refs.o\n> \n>    ...\n>    $\n> \n> The '-Wold-style-declaration' gcc warning flag is not a standard\n> project flag, and I can't quite remember why I have it set, so I\n> guess you could just ignore it. However, all other 'static inline'\n> functions in the project have the inline keyword before the return\n> type, so ... ;-)\n> \n> Also, sparse spewed 40 warnings for t/helper/test-json-writer.c,\n> which were mainly about file-local symbols, but had a couple of\n> 'constant is so large ...', like so:\n> \n>    $ grep warning psp-out | head -8\n>    t/helper/test-json-writer.c:45:46: warning: constant 0xffffffffffffffff is so big it is unsigned long\n>    t/helper/test-json-writer.c:108:40: warning: constant 0xffffffffffffffff is so big it is unsigned long\n>    t/helper/test-json-writer.c:4:12: warning: symbol 'expect_obj1' was not declared. Should it be static?\n>    t/helper/test-json-writer.c:5:12: warning: symbol 'expect_obj2' was not declared. Should it be static?\n>    t/helper/test-json-writer.c:6:12: warning: symbol 'expect_obj3' was not declared. Should it be static?\n>    t/helper/test-json-writer.c:7:12: warning: symbol 'expect_obj4' was not declared. Should it be static?\n>    t/helper/test-json-writer.c:8:12: warning: symbol 'expect_obj5' was not declared. Should it be static?\n>    t/helper/test-json-writer.c:10:20: warning: symbol 'obj1' was not declared. Should it be static?\n>    $\n> \n> I decided to use the UINT64_C(v) macro from <stdint.h>, which is\n> a C99 feature, and will (hopefully) not be a problem.\n> \n> ATB,\n> Ramsay Jones\n> \n> -- >8 --\n> Subject: [PATCH] json-writer: fix up gcc and sparse warnings\n> \n> Signed-off-by: Ramsay Jones <ramsay@ramsayjones.plus.com>\n> ---\n>   json-writer.c               |  8 ++---\n>   t/helper/test-json-writer.c | 80 ++++++++++++++++++++++-----------------------\n>   2 files changed, 44 insertions(+), 44 deletions(-)\n> \n> diff --git a/json-writer.c b/json-writer.c\n> index 89a6abb57..ba0365d20 100644\n> --- a/json-writer.c\n> +++ b/json-writer.c\n> @@ -50,7 +50,7 @@ static inline void begin(struct json_writer *jw, int is_array)\n>   /*\n>    * Assert that we have an open object at this level.\n>    */\n> -static void inline assert_in_object(const struct json_writer *jw, const char *key)\n> +static inline void assert_in_object(const struct json_writer *jw, const char *key)\n>   {\n>   \tif (!jw->nr)\n>   \t\tBUG(\"object: missing jw_object_begin(): '%s'\", key);\n> @@ -61,7 +61,7 @@ static void inline assert_in_object(const struct json_writer *jw, const char *ke\n>   /*\n>    * Assert that we have an open array at this level.\n>    */\n> -static void inline assert_in_array(const struct json_writer *jw)\n> +static inline void assert_in_array(const struct json_writer *jw)\n>   {\n>   \tif (!jw->nr)\n>   \t\tBUG(\"array: missing jw_begin()\");\n> @@ -72,7 +72,7 @@ static void inline assert_in_array(const struct json_writer *jw)\n>   /*\n>    * Add comma if we have already seen a member at this level.\n>    */\n> -static void inline maybe_add_comma(struct json_writer *jw)\n> +static inline void maybe_add_comma(struct json_writer *jw)\n>   {\n>   \tif (jw->levels[jw->nr - 1].level_is_empty)\n>   \t\tjw->levels[jw->nr - 1].level_is_empty = 0;\n> @@ -84,7 +84,7 @@ static void inline maybe_add_comma(struct json_writer *jw)\n>    * Assert that the given JSON object or JSON array has been properly\n>    * terminated.  (Has closing bracket.)\n>    */\n> -static void inline assert_is_terminated(const struct json_writer *jw)\n> +static inline void assert_is_terminated(const struct json_writer *jw)\n>   {\n>   \tif (jw->nr)\n>   \t\tBUG(\"object: missing jw_end(): '%s'\", jw->json.buf);\n> diff --git a/t/helper/test-json-writer.c b/t/helper/test-json-writer.c\n> index 4bf815c2d..9218ee9df 100644\n> --- a/t/helper/test-json-writer.c\n> +++ b/t/helper/test-json-writer.c\n> @@ -1,20 +1,20 @@\n>   #include \"cache.h\"\n>   #include \"json-writer.h\"\n>   \n> -const char *expect_obj1 = \"{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true}\";\n> -const char *expect_obj2 = \"{\\\"a\\\":-1,\\\"b\\\":2147483647,\\\"c\\\":0}\";\n> -const char *expect_obj3 = \"{\\\"a\\\":0,\\\"b\\\":4294967295,\\\"c\\\":18446744073709551615}\";\n> -const char *expect_obj4 = \"{\\\"t\\\":true,\\\"f\\\":false,\\\"n\\\":null}\";\n> -const char *expect_obj5 = \"{\\\"abc\\\\tdef\\\":\\\"abc\\\\\\\\def\\\"}\";\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>   \n> -struct json_writer obj1 = JSON_WRITER_INIT;\n> -struct json_writer obj2 = JSON_WRITER_INIT;\n> -struct json_writer obj3 = JSON_WRITER_INIT;\n> -struct json_writer obj4 = JSON_WRITER_INIT;\n> -struct json_writer obj5 = JSON_WRITER_INIT;\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>   \n>   \n> -void make_obj1(void)\n> +static void make_obj1(void)\n>   {\n>   \tjw_object_begin(&obj1);\n>   \t{\n> @@ -25,7 +25,7 @@ void make_obj1(void)\n>   \tjw_end(&obj1);\n>   }\n>   \n> -void make_obj2(void)\n> +static void make_obj2(void)\n>   {\n>   \tjw_object_begin(&obj2);\n>   \t{\n> @@ -36,18 +36,18 @@ void make_obj2(void)\n>   \tjw_end(&obj2);\n>   }\n>   \n> -void make_obj3(void)\n> +static void make_obj3(void)\n>   {\n>   \tjw_object_begin(&obj3);\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\", 0xffffffffffffffff);\n> +\t\tjw_object_uint64(&obj3, \"c\", UINT64_C(0xffffffffffffffff));\n>   \t}\n>   \tjw_end(&obj3);\n>   }\n>   \n> -void make_obj4(void)\n> +static void make_obj4(void)\n>   {\n>   \tjw_object_begin(&obj4);\n>   \t{\n> @@ -58,7 +58,7 @@ void make_obj4(void)\n>   \tjw_end(&obj4);\n>   }\n>   \n> -void make_obj5(void)\n> +static void make_obj5(void)\n>   {\n>   \tjw_object_begin(&obj5);\n>   \t{\n> @@ -67,17 +67,17 @@ void make_obj5(void)\n>   \tjw_end(&obj5);\n>   }\n>   \n> -const char *expect_arr1 = \"[\\\"abc\\\",42,true]\";\n> -const char *expect_arr2 = \"[-1,2147483647,0]\";\n> -const char *expect_arr3 = \"[0,4294967295,18446744073709551615]\";\n> -const char *expect_arr4 = \"[true,false,null]\";\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> -struct json_writer arr1 = JSON_WRITER_INIT;\n> -struct json_writer arr2 = JSON_WRITER_INIT;\n> -struct json_writer arr3 = JSON_WRITER_INIT;\n> -struct json_writer arr4 = JSON_WRITER_INIT;\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> -void make_arr1(void)\n> +static void make_arr1(void)\n>   {\n>   \tjw_array_begin(&arr1);\n>   \t{\n> @@ -88,7 +88,7 @@ void make_arr1(void)\n>   \tjw_end(&arr1);\n>   }\n>   \n> -void make_arr2(void)\n> +static void make_arr2(void)\n>   {\n>   \tjw_array_begin(&arr2);\n>   \t{\n> @@ -99,18 +99,18 @@ void make_arr2(void)\n>   \tjw_end(&arr2);\n>   }\n>   \n> -void make_arr3(void)\n> +static void make_arr3(void)\n>   {\n>   \tjw_array_begin(&arr3);\n>   \t{\n>   \t\tjw_array_uint64(&arr3, 0);\n>   \t\tjw_array_uint64(&arr3, 0xffffffff);\n> -\t\tjw_array_uint64(&arr3, 0xffffffffffffffff);\n> +\t\tjw_array_uint64(&arr3, UINT64_C(0xffffffffffffffff));\n>   \t}\n>   \tjw_end(&arr3);\n>   }\n>   \n> -void make_arr4(void)\n> +static void make_arr4(void)\n>   {\n>   \tjw_array_begin(&arr4);\n>   \t{\n> @@ -121,12 +121,12 @@ void make_arr4(void)\n>   \tjw_end(&arr4);\n>   }\n>   \n> -char *expect_nest1 =\n> +static char *expect_nest1 =\n>   \t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n>   \n> -struct json_writer nest1 = JSON_WRITER_INIT;\n> +static struct json_writer nest1 = JSON_WRITER_INIT;\n>   \n> -void make_nest1(void)\n> +static void make_nest1(void)\n>   {\n>   \tjw_object_begin(&nest1);\n>   \t{\n> @@ -136,12 +136,12 @@ void make_nest1(void)\n>   \tjw_end(&nest1);\n>   }\n>   \n> -char *expect_inline1 =\n> +static char *expect_inline1 =\n>   \t\"{\\\"obj1\\\":{\\\"a\\\":\\\"abc\\\",\\\"b\\\":42,\\\"c\\\":true},\\\"arr1\\\":[\\\"abc\\\",42,true]}\";\n> -struct json_writer inline1 = JSON_WRITER_INIT;\n> +static struct json_writer inline1 = JSON_WRITER_INIT;\n>   \n>   \n> -void make_inline1(void)\n> +static void make_inline1(void)\n>   {\n>   \tjw_object_begin(&inline1);\n>   \t{\n> @@ -163,11 +163,11 @@ void make_inline1(void)\n>   \tjw_end(&inline1);\n>   }\n>   \n> -char *expect_inline2 =\n> +static char *expect_inline2 =\n>   \t\"[[1,2],[3,4],{\\\"a\\\":\\\"abc\\\"}]\";\n> -struct json_writer inline2 = JSON_WRITER_INIT;\n> +static struct json_writer inline2 = JSON_WRITER_INIT;\n>   \n> -void make_inline2(void)\n> +static void make_inline2(void)\n>   {\n>   \tjw_array_begin(&inline2);\n>   \t{\n> @@ -193,7 +193,7 @@ void make_inline2(void)\n>   }\n>   \n>   \n> -void cmp(const char *test, const struct json_writer *jw, const char *exp)\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> @@ -209,7 +209,7 @@ void cmp(const char *test, const struct json_writer *jw, const char *exp)\n>    * Run some basic regression tests with some known patterns.\n>    * These tests also demonstrate how to use the jw_ API.\n>    */\n> -int unit_tests(void)\n> +static int unit_tests(void)\n>   {\n>   \tt(obj1);\n>   \tt(obj2);\n> \n"}]}