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