git

Форк
0
/
json-writer.c 
409 строк · 8.5 Кб
1
#include "git-compat-util.h"
2
#include "json-writer.h"
3

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

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
 */
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

47
static void indent_pretty(struct json_writer *jw)
48
{
49
	strbuf_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
 */
56
static void begin(struct json_writer *jw, char ch_open, int pretty)
57
{
58
	jw->pretty = pretty;
59

60
	strbuf_addch(&jw->json, ch_open);
61

62
	strbuf_addch(&jw->open_stack, ch_open);
63
	jw->need_comma = 0;
64
}
65

66
/*
67
 * Assert that the top of the open-stack is an object.
68
 */
69
static void assert_in_object(const struct json_writer *jw, const char *key)
70
{
71
	if (!jw->open_stack.len)
72
		BUG("json-writer: object: missing jw_object_begin(): '%s'", key);
73
	if (jw->open_stack.buf[jw->open_stack.len - 1] != '{')
74
		BUG("json-writer: object: not in object: '%s'", key);
75
}
76

77
/*
78
 * Assert that the top of the open-stack is an array.
79
 */
80
static void assert_in_array(const struct json_writer *jw)
81
{
82
	if (!jw->open_stack.len)
83
		BUG("json-writer: array: missing jw_array_begin()");
84
	if (jw->open_stack.buf[jw->open_stack.len - 1] != '[')
85
		BUG("json-writer: array: not in array");
86
}
87

88
/*
89
 * Add comma if we have already seen a member at this level.
90
 */
91
static void maybe_add_comma(struct json_writer *jw)
92
{
93
	if (jw->need_comma)
94
		strbuf_addch(&jw->json, ',');
95
	else
96
		jw->need_comma = 1;
97
}
98

99
static void fmt_double(struct json_writer *jw, int precision,
100
			      double value)
101
{
102
	if (precision < 0) {
103
		strbuf_addf(&jw->json, "%f", value);
104
	} else {
105
		struct strbuf fmt = STRBUF_INIT;
106
		strbuf_addf(&fmt, "%%.%df", precision);
107
		strbuf_addf(&jw->json, fmt.buf, value);
108
		strbuf_release(&fmt);
109
	}
110
}
111

112
static void object_common(struct json_writer *jw, const char *key)
113
{
114
	assert_in_object(jw, key);
115
	maybe_add_comma(jw);
116

117
	if (jw->pretty) {
118
		strbuf_addch(&jw->json, '\n');
119
		indent_pretty(jw);
120
	}
121

122
	append_quoted_string(&jw->json, key);
123
	strbuf_addch(&jw->json, ':');
124
	if (jw->pretty)
125
		strbuf_addch(&jw->json, ' ');
126
}
127

128
static void array_common(struct json_writer *jw)
129
{
130
	assert_in_array(jw);
131
	maybe_add_comma(jw);
132

133
	if (jw->pretty) {
134
		strbuf_addch(&jw->json, '\n');
135
		indent_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
 */
143
static void assert_is_terminated(const struct json_writer *jw)
144
{
145
	if (jw->open_stack.len)
146
		BUG("json-writer: object: missing jw_end(): '%s'",
147
		    jw->json.buf);
148
}
149

150
void jw_object_begin(struct json_writer *jw, int pretty)
151
{
152
	begin(jw, '{', pretty);
153
}
154

155
void jw_object_string(struct json_writer *jw, const char *key, const char *value)
156
{
157
	object_common(jw, key);
158
	append_quoted_string(&jw->json, value);
159
}
160

161
void jw_object_intmax(struct json_writer *jw, const char *key, intmax_t value)
162
{
163
	object_common(jw, key);
164
	strbuf_addf(&jw->json, "%"PRIdMAX, value);
165
}
166

167
void jw_object_double(struct json_writer *jw, const char *key, int precision,
168
		      double value)
169
{
170
	object_common(jw, key);
171
	fmt_double(jw, precision, value);
172
}
173

174
void jw_object_true(struct json_writer *jw, const char *key)
175
{
176
	object_common(jw, key);
177
	strbuf_addstr(&jw->json, "true");
178
}
179

180
void jw_object_false(struct json_writer *jw, const char *key)
181
{
182
	object_common(jw, key);
183
	strbuf_addstr(&jw->json, "false");
184
}
185

186
void jw_object_bool(struct json_writer *jw, const char *key, int value)
187
{
188
	if (value)
189
		jw_object_true(jw, key);
190
	else
191
		jw_object_false(jw, key);
192
}
193

194
void jw_object_null(struct json_writer *jw, const char *key)
195
{
196
	object_common(jw, key);
197
	strbuf_addstr(&jw->json, "null");
198
}
199

200
static void increase_indent(struct strbuf *sb,
201
			    const struct json_writer *jw,
202
			    int indent)
203
{
204
	int k;
205

206
	strbuf_reset(sb);
207
	for (k = 0; k < jw->json.len; k++) {
208
		char ch = jw->json.buf[k];
209
		strbuf_addch(sb, ch);
210
		if (ch == '\n')
211
			strbuf_addchars(sb, ' ', indent);
212
	}
213
}
214

215
static void kill_indent(struct strbuf *sb,
216
			const struct json_writer *jw)
217
{
218
	int k;
219
	int eat_it = 0;
220

221
	strbuf_reset(sb);
222
	for (k = 0; k < jw->json.len; k++) {
223
		char ch = jw->json.buf[k];
224
		if (eat_it && ch == ' ')
225
			continue;
226
		if (ch == '\n') {
227
			eat_it = 1;
228
			continue;
229
		}
230
		eat_it = 0;
231
		strbuf_addch(sb, ch);
232
	}
233
}
234

235
static void append_sub_jw(struct json_writer *jw,
236
			  const 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
	 */
251
	if (jw->pretty && jw->open_stack.len && value->pretty) {
252
		struct strbuf sb = STRBUF_INIT;
253
		increase_indent(&sb, value, jw->open_stack.len * 2);
254
		strbuf_addbuf(&jw->json, &sb);
255
		strbuf_release(&sb);
256
		return;
257
	}
258
	if (!jw->pretty && value->pretty) {
259
		struct strbuf sb = STRBUF_INIT;
260
		kill_indent(&sb, value);
261
		strbuf_addbuf(&jw->json, &sb);
262
		strbuf_release(&sb);
263
		return;
264
	}
265

266
	strbuf_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
 */
273
void jw_object_sub_jw(struct json_writer *jw, const char *key,
274
		      const struct json_writer *value)
275
{
276
	assert_is_terminated(value);
277

278
	object_common(jw, key);
279
	append_sub_jw(jw, value);
280
}
281

282
void jw_object_inline_begin_object(struct json_writer *jw, const char *key)
283
{
284
	object_common(jw, key);
285

286
	jw_object_begin(jw, jw->pretty);
287
}
288

289
void jw_object_inline_begin_array(struct json_writer *jw, const char *key)
290
{
291
	object_common(jw, key);
292

293
	jw_array_begin(jw, jw->pretty);
294
}
295

296
void jw_array_begin(struct json_writer *jw, int pretty)
297
{
298
	begin(jw, '[', pretty);
299
}
300

301
void jw_array_string(struct json_writer *jw, const char *value)
302
{
303
	array_common(jw);
304
	append_quoted_string(&jw->json, value);
305
}
306

307
void jw_array_intmax(struct json_writer *jw, intmax_t value)
308
{
309
	array_common(jw);
310
	strbuf_addf(&jw->json, "%"PRIdMAX, value);
311
}
312

313
void jw_array_double(struct json_writer *jw, int precision, double value)
314
{
315
	array_common(jw);
316
	fmt_double(jw, precision, value);
317
}
318

319
void jw_array_true(struct json_writer *jw)
320
{
321
	array_common(jw);
322
	strbuf_addstr(&jw->json, "true");
323
}
324

325
void jw_array_false(struct json_writer *jw)
326
{
327
	array_common(jw);
328
	strbuf_addstr(&jw->json, "false");
329
}
330

331
void jw_array_bool(struct json_writer *jw, int value)
332
{
333
	if (value)
334
		jw_array_true(jw);
335
	else
336
		jw_array_false(jw);
337
}
338

339
void jw_array_null(struct json_writer *jw)
340
{
341
	array_common(jw);
342
	strbuf_addstr(&jw->json, "null");
343
}
344

345
void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value)
346
{
347
	assert_is_terminated(value);
348

349
	array_common(jw);
350
	append_sub_jw(jw, value);
351
}
352

353
void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv)
354
{
355
	int k;
356

357
	for (k = 0; k < argc; k++)
358
		jw_array_string(jw, argv[k]);
359
}
360

361
void jw_array_argv(struct json_writer *jw, const char **argv)
362
{
363
	while (*argv)
364
		jw_array_string(jw, *argv++);
365
}
366

367
void jw_array_inline_begin_object(struct json_writer *jw)
368
{
369
	array_common(jw);
370

371
	jw_object_begin(jw, jw->pretty);
372
}
373

374
void jw_array_inline_begin_array(struct json_writer *jw)
375
{
376
	array_common(jw);
377

378
	jw_array_begin(jw, jw->pretty);
379
}
380

381
int jw_is_terminated(const struct json_writer *jw)
382
{
383
	return !jw->open_stack.len;
384
}
385

386
void jw_end(struct json_writer *jw)
387
{
388
	char ch_open;
389
	int len;
390

391
	if (!jw->open_stack.len)
392
		BUG("json-writer: too many jw_end(): '%s'", jw->json.buf);
393

394
	len = jw->open_stack.len - 1;
395
	ch_open = jw->open_stack.buf[len];
396

397
	strbuf_setlen(&jw->open_stack, len);
398
	jw->need_comma = 1;
399

400
	if (jw->pretty) {
401
		strbuf_addch(&jw->json, '\n');
402
		indent_pretty(jw);
403
	}
404

405
	if (ch_open == '{')
406
		strbuf_addch(&jw->json, '}');
407
	else
408
		strbuf_addch(&jw->json, ']');
409
}
410

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.