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