1 #include "cache.h"
2 #include "json-writer.h"
3 
jw_init(struct json_writer * jw)4 void jw_init(struct json_writer *jw)
5 {
6 	struct json_writer blank = JSON_WRITER_INIT;
7 	memcpy(jw, &blank, sizeof(*jw));;
8 }
9 
jw_release(struct json_writer * jw)10 void 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  */
append_quoted_string(struct strbuf * out,const char * in)19 static 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 
indent_pretty(struct json_writer * jw)47 static 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  */
begin(struct json_writer * jw,char ch_open,int pretty)59 static 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  */
assert_in_object(const struct json_writer * jw,const char * key)72 static 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  */
assert_in_array(const struct json_writer * jw)83 static 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  */
maybe_add_comma(struct json_writer * jw)94 static 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 
fmt_double(struct json_writer * jw,int precision,double value)102 static 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 
object_common(struct json_writer * jw,const char * key)115 static 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 
array_common(struct json_writer * jw)131 static 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  */
assert_is_terminated(const struct json_writer * jw)146 static 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 
jw_object_begin(struct json_writer * jw,int pretty)153 void jw_object_begin(struct json_writer *jw, int pretty)
154 {
155 	begin(jw, '{', pretty);
156 }
157 
jw_object_string(struct json_writer * jw,const char * key,const char * value)158 void 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 
jw_object_intmax(struct json_writer * jw,const char * key,intmax_t value)164 void 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 
jw_object_double(struct json_writer * jw,const char * key,int precision,double value)170 void 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 
jw_object_true(struct json_writer * jw,const char * key)177 void jw_object_true(struct json_writer *jw, const char *key)
178 {
179 	object_common(jw, key);
180 	strbuf_addstr(&jw->json, "true");
181 }
182 
jw_object_false(struct json_writer * jw,const char * key)183 void jw_object_false(struct json_writer *jw, const char *key)
184 {
185 	object_common(jw, key);
186 	strbuf_addstr(&jw->json, "false");
187 }
188 
jw_object_bool(struct json_writer * jw,const char * key,int value)189 void 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 
jw_object_null(struct json_writer * jw,const char * key)197 void jw_object_null(struct json_writer *jw, const char *key)
198 {
199 	object_common(jw, key);
200 	strbuf_addstr(&jw->json, "null");
201 }
202 
increase_indent(struct strbuf * sb,const struct json_writer * jw,int indent)203 static 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 
kill_indent(struct strbuf * sb,const struct json_writer * jw)218 static 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 
append_sub_jw(struct json_writer * jw,const struct json_writer * value)238 static 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  */
jw_object_sub_jw(struct json_writer * jw,const char * key,const struct json_writer * value)276 void 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 
jw_object_inline_begin_object(struct json_writer * jw,const char * key)285 void 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 
jw_object_inline_begin_array(struct json_writer * jw,const char * key)292 void 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 
jw_array_begin(struct json_writer * jw,int pretty)299 void jw_array_begin(struct json_writer *jw, int pretty)
300 {
301 	begin(jw, '[', pretty);
302 }
303 
jw_array_string(struct json_writer * jw,const char * value)304 void jw_array_string(struct json_writer *jw, const char *value)
305 {
306 	array_common(jw);
307 	append_quoted_string(&jw->json, value);
308 }
309 
jw_array_intmax(struct json_writer * jw,intmax_t value)310 void jw_array_intmax(struct json_writer *jw, intmax_t value)
311 {
312 	array_common(jw);
313 	strbuf_addf(&jw->json, "%"PRIdMAX, value);
314 }
315 
jw_array_double(struct json_writer * jw,int precision,double value)316 void jw_array_double(struct json_writer *jw, int precision, double value)
317 {
318 	array_common(jw);
319 	fmt_double(jw, precision, value);
320 }
321 
jw_array_true(struct json_writer * jw)322 void jw_array_true(struct json_writer *jw)
323 {
324 	array_common(jw);
325 	strbuf_addstr(&jw->json, "true");
326 }
327 
jw_array_false(struct json_writer * jw)328 void jw_array_false(struct json_writer *jw)
329 {
330 	array_common(jw);
331 	strbuf_addstr(&jw->json, "false");
332 }
333 
jw_array_bool(struct json_writer * jw,int value)334 void 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 
jw_array_null(struct json_writer * jw)342 void jw_array_null(struct json_writer *jw)
343 {
344 	array_common(jw);
345 	strbuf_addstr(&jw->json, "null");
346 }
347 
jw_array_sub_jw(struct json_writer * jw,const struct json_writer * value)348 void 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 
jw_array_argc_argv(struct json_writer * jw,int argc,const char ** argv)356 void 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 
jw_array_argv(struct json_writer * jw,const char ** argv)364 void jw_array_argv(struct json_writer *jw, const char **argv)
365 {
366 	while (*argv)
367 		jw_array_string(jw, *argv++);
368 }
369 
jw_array_inline_begin_object(struct json_writer * jw)370 void jw_array_inline_begin_object(struct json_writer *jw)
371 {
372 	array_common(jw);
373 
374 	jw_object_begin(jw, jw->pretty);
375 }
376 
jw_array_inline_begin_array(struct json_writer * jw)377 void jw_array_inline_begin_array(struct json_writer *jw)
378 {
379 	array_common(jw);
380 
381 	jw_array_begin(jw, jw->pretty);
382 }
383 
jw_is_terminated(const struct json_writer * jw)384 int jw_is_terminated(const struct json_writer *jw)
385 {
386 	return !jw->open_stack.len;
387 }
388 
jw_end(struct json_writer * jw)389 void 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 }
413