Raw
1 #define DISABLE_SIGN_COMPARE_WARNINGS
2
3 #include "git-compat-util.h"
4 #include "json-writer.h"
5
6 void jw_init(struct json_writer *jw)
7 {
8 struct json_writer blank = JSON_WRITER_INIT;
9 memcpy(jw, &blank, sizeof(*jw));;
10 }
11
12 void 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 */
21 static 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
49 static 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 */
58 static 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 */
71 static 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 */
82 static 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 */
93 static 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
101 static 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
114 static 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
130 static 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 */
145 static 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
152 void jw_object_begin(struct json_writer *jw, int pretty)
153 {
154 begin(jw, '{', pretty);
155 }
156
157 void 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
163 void 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
169 void 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
176 void jw_object_true(struct json_writer *jw, const char *key)
177 {
178 object_common(jw, key);
179 strbuf_addstr(&jw->json, "true");
180 }
181
182 void jw_object_false(struct json_writer *jw, const char *key)
183 {
184 object_common(jw, key);
185 strbuf_addstr(&jw->json, "false");
186 }
187
188 void 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
196 void jw_object_null(struct json_writer *jw, const char *key)
197 {
198 object_common(jw, key);
199 strbuf_addstr(&jw->json, "null");
200 }
201
202 static 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
217 static 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
237 static 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
271 void 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
280 void 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
287 void 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
294 void jw_array_begin(struct json_writer *jw, int pretty)
295 {
296 begin(jw, '[', pretty);
297 }
298
299 void jw_array_string(struct json_writer *jw, const char *value)
300 {
301 array_common(jw);
302 append_quoted_string(&jw->json, value);
303 }
304
305 void jw_array_intmax(struct json_writer *jw, intmax_t value)
306 {
307 array_common(jw);
308 strbuf_addf(&jw->json, "%"PRIdMAX, value);
309 }
310
311 void jw_array_double(struct json_writer *jw, int precision, double value)
312 {
313 array_common(jw);
314 fmt_double(jw, precision, value);
315 }
316
317 void jw_array_true(struct json_writer *jw)
318 {
319 array_common(jw);
320 strbuf_addstr(&jw->json, "true");
321 }
322
323 void jw_array_false(struct json_writer *jw)
324 {
325 array_common(jw);
326 strbuf_addstr(&jw->json, "false");
327 }
328
329 void 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
337 void jw_array_null(struct json_writer *jw)
338 {
339 array_common(jw);
340 strbuf_addstr(&jw->json, "null");
341 }
342
343 void 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
351 void 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
359 void jw_array_argv(struct json_writer *jw, const char **argv)
360 {
361 while (*argv)
362 jw_array_string(jw, *argv++);
363 }
364
365 void jw_array_inline_begin_object(struct json_writer *jw)
366 {
367 array_common(jw);
368
369 jw_object_begin(jw, jw->pretty);
370 }
371
372 void jw_array_inline_begin_array(struct json_writer *jw)
373 {
374 array_common(jw);
375
376 jw_array_begin(jw, jw->pretty);
377 }
378
379 int jw_is_terminated(const struct json_writer *jw)
380 {
381 return !jw->open_stack.len;
382 }
383
384 void 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 }