git
/
json-writer.c
409 строк · 8.5 Кб
1#include "git-compat-util.h"2#include "json-writer.h"3
4void jw_init(struct json_writer *jw)5{
6struct json_writer blank = JSON_WRITER_INIT;7memcpy(jw, &blank, sizeof(*jw));;8}
9
10void jw_release(struct json_writer *jw)11{
12strbuf_release(&jw->json);13strbuf_release(&jw->open_stack);14}
15
16/*
17* Append JSON-quoted version of the given string to 'out'.
18*/
19static void append_quoted_string(struct strbuf *out, const char *in)20{
21unsigned char c;22
23strbuf_addch(out, '"');24while ((c = *in++) != '\0') {25if (c == '"')26strbuf_addstr(out, "\\\"");27else if (c == '\\')28strbuf_addstr(out, "\\\\");29else if (c == '\n')30strbuf_addstr(out, "\\n");31else if (c == '\r')32strbuf_addstr(out, "\\r");33else if (c == '\t')34strbuf_addstr(out, "\\t");35else if (c == '\f')36strbuf_addstr(out, "\\f");37else if (c == '\b')38strbuf_addstr(out, "\\b");39else if (c < 0x20)40strbuf_addf(out, "\\u%04x", c);41else42strbuf_addch(out, c);43}44strbuf_addch(out, '"');45}
46
47static void indent_pretty(struct json_writer *jw)48{
49strbuf_addstrings(&jw->json, " ", jw->open_stack.len);50}
51
52/*
53* Begin an object or array (either top-level or nested within the currently
54* open object or array).
55*/
56static void begin(struct json_writer *jw, char ch_open, int pretty)57{
58jw->pretty = pretty;59
60strbuf_addch(&jw->json, ch_open);61
62strbuf_addch(&jw->open_stack, ch_open);63jw->need_comma = 0;64}
65
66/*
67* Assert that the top of the open-stack is an object.
68*/
69static void assert_in_object(const struct json_writer *jw, const char *key)70{
71if (!jw->open_stack.len)72BUG("json-writer: object: missing jw_object_begin(): '%s'", key);73if (jw->open_stack.buf[jw->open_stack.len - 1] != '{')74BUG("json-writer: object: not in object: '%s'", key);75}
76
77/*
78* Assert that the top of the open-stack is an array.
79*/
80static void assert_in_array(const struct json_writer *jw)81{
82if (!jw->open_stack.len)83BUG("json-writer: array: missing jw_array_begin()");84if (jw->open_stack.buf[jw->open_stack.len - 1] != '[')85BUG("json-writer: array: not in array");86}
87
88/*
89* Add comma if we have already seen a member at this level.
90*/
91static void maybe_add_comma(struct json_writer *jw)92{
93if (jw->need_comma)94strbuf_addch(&jw->json, ',');95else96jw->need_comma = 1;97}
98
99static void fmt_double(struct json_writer *jw, int precision,100double value)101{
102if (precision < 0) {103strbuf_addf(&jw->json, "%f", value);104} else {105struct strbuf fmt = STRBUF_INIT;106strbuf_addf(&fmt, "%%.%df", precision);107strbuf_addf(&jw->json, fmt.buf, value);108strbuf_release(&fmt);109}110}
111
112static void object_common(struct json_writer *jw, const char *key)113{
114assert_in_object(jw, key);115maybe_add_comma(jw);116
117if (jw->pretty) {118strbuf_addch(&jw->json, '\n');119indent_pretty(jw);120}121
122append_quoted_string(&jw->json, key);123strbuf_addch(&jw->json, ':');124if (jw->pretty)125strbuf_addch(&jw->json, ' ');126}
127
128static void array_common(struct json_writer *jw)129{
130assert_in_array(jw);131maybe_add_comma(jw);132
133if (jw->pretty) {134strbuf_addch(&jw->json, '\n');135indent_pretty(jw);136}137}
138
139/*
140* Assert that the given JSON object or JSON array has been properly
141* terminated. (Has closing bracket.)
142*/
143static void assert_is_terminated(const struct json_writer *jw)144{
145if (jw->open_stack.len)146BUG("json-writer: object: missing jw_end(): '%s'",147jw->json.buf);148}
149
150void jw_object_begin(struct json_writer *jw, int pretty)151{
152begin(jw, '{', pretty);153}
154
155void jw_object_string(struct json_writer *jw, const char *key, const char *value)156{
157object_common(jw, key);158append_quoted_string(&jw->json, value);159}
160
161void jw_object_intmax(struct json_writer *jw, const char *key, intmax_t value)162{
163object_common(jw, key);164strbuf_addf(&jw->json, "%"PRIdMAX, value);165}
166
167void jw_object_double(struct json_writer *jw, const char *key, int precision,168double value)169{
170object_common(jw, key);171fmt_double(jw, precision, value);172}
173
174void jw_object_true(struct json_writer *jw, const char *key)175{
176object_common(jw, key);177strbuf_addstr(&jw->json, "true");178}
179
180void jw_object_false(struct json_writer *jw, const char *key)181{
182object_common(jw, key);183strbuf_addstr(&jw->json, "false");184}
185
186void jw_object_bool(struct json_writer *jw, const char *key, int value)187{
188if (value)189jw_object_true(jw, key);190else191jw_object_false(jw, key);192}
193
194void jw_object_null(struct json_writer *jw, const char *key)195{
196object_common(jw, key);197strbuf_addstr(&jw->json, "null");198}
199
200static void increase_indent(struct strbuf *sb,201const struct json_writer *jw,202int indent)203{
204int k;205
206strbuf_reset(sb);207for (k = 0; k < jw->json.len; k++) {208char ch = jw->json.buf[k];209strbuf_addch(sb, ch);210if (ch == '\n')211strbuf_addchars(sb, ' ', indent);212}213}
214
215static void kill_indent(struct strbuf *sb,216const struct json_writer *jw)217{
218int k;219int eat_it = 0;220
221strbuf_reset(sb);222for (k = 0; k < jw->json.len; k++) {223char ch = jw->json.buf[k];224if (eat_it && ch == ' ')225continue;226if (ch == '\n') {227eat_it = 1;228continue;229}230eat_it = 0;231strbuf_addch(sb, ch);232}233}
234
235static void append_sub_jw(struct json_writer *jw,236const struct json_writer *value)237{
238/*239* If both are pretty, increase the indentation of the sub_jw
240* to better fit under the super.
241*
242* If the super is pretty, but the sub_jw is compact, leave the
243* sub_jw compact. (We don't want to parse and rebuild the sub_jw
244* for this debug-ish feature.)
245*
246* If the super is compact, and the sub_jw is pretty, convert
247* the sub_jw to compact.
248*
249* If both are compact, keep the sub_jw compact.
250*/
251if (jw->pretty && jw->open_stack.len && value->pretty) {252struct strbuf sb = STRBUF_INIT;253increase_indent(&sb, value, jw->open_stack.len * 2);254strbuf_addbuf(&jw->json, &sb);255strbuf_release(&sb);256return;257}258if (!jw->pretty && value->pretty) {259struct strbuf sb = STRBUF_INIT;260kill_indent(&sb, value);261strbuf_addbuf(&jw->json, &sb);262strbuf_release(&sb);263return;264}265
266strbuf_addbuf(&jw->json, &value->json);267}
268
269/*
270* Append existing (properly terminated) JSON sub-data (object or array)
271* as-is onto the given JSON data.
272*/
273void jw_object_sub_jw(struct json_writer *jw, const char *key,274const struct json_writer *value)275{
276assert_is_terminated(value);277
278object_common(jw, key);279append_sub_jw(jw, value);280}
281
282void jw_object_inline_begin_object(struct json_writer *jw, const char *key)283{
284object_common(jw, key);285
286jw_object_begin(jw, jw->pretty);287}
288
289void jw_object_inline_begin_array(struct json_writer *jw, const char *key)290{
291object_common(jw, key);292
293jw_array_begin(jw, jw->pretty);294}
295
296void jw_array_begin(struct json_writer *jw, int pretty)297{
298begin(jw, '[', pretty);299}
300
301void jw_array_string(struct json_writer *jw, const char *value)302{
303array_common(jw);304append_quoted_string(&jw->json, value);305}
306
307void jw_array_intmax(struct json_writer *jw, intmax_t value)308{
309array_common(jw);310strbuf_addf(&jw->json, "%"PRIdMAX, value);311}
312
313void jw_array_double(struct json_writer *jw, int precision, double value)314{
315array_common(jw);316fmt_double(jw, precision, value);317}
318
319void jw_array_true(struct json_writer *jw)320{
321array_common(jw);322strbuf_addstr(&jw->json, "true");323}
324
325void jw_array_false(struct json_writer *jw)326{
327array_common(jw);328strbuf_addstr(&jw->json, "false");329}
330
331void jw_array_bool(struct json_writer *jw, int value)332{
333if (value)334jw_array_true(jw);335else336jw_array_false(jw);337}
338
339void jw_array_null(struct json_writer *jw)340{
341array_common(jw);342strbuf_addstr(&jw->json, "null");343}
344
345void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value)346{
347assert_is_terminated(value);348
349array_common(jw);350append_sub_jw(jw, value);351}
352
353void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv)354{
355int k;356
357for (k = 0; k < argc; k++)358jw_array_string(jw, argv[k]);359}
360
361void jw_array_argv(struct json_writer *jw, const char **argv)362{
363while (*argv)364jw_array_string(jw, *argv++);365}
366
367void jw_array_inline_begin_object(struct json_writer *jw)368{
369array_common(jw);370
371jw_object_begin(jw, jw->pretty);372}
373
374void jw_array_inline_begin_array(struct json_writer *jw)375{
376array_common(jw);377
378jw_array_begin(jw, jw->pretty);379}
380
381int jw_is_terminated(const struct json_writer *jw)382{
383return !jw->open_stack.len;384}
385
386void jw_end(struct json_writer *jw)387{
388char ch_open;389int len;390
391if (!jw->open_stack.len)392BUG("json-writer: too many jw_end(): '%s'", jw->json.buf);393
394len = jw->open_stack.len - 1;395ch_open = jw->open_stack.buf[len];396
397strbuf_setlen(&jw->open_stack, len);398jw->need_comma = 1;399
400if (jw->pretty) {401strbuf_addch(&jw->json, '\n');402indent_pretty(jw);403}404
405if (ch_open == '{')406strbuf_addch(&jw->json, '}');407else408strbuf_addch(&jw->json, ']');409}
410