Coverage Report

Created: 2024-09-08 06:23

/src/git/json-writer.c
Line
Count
Source (jump to first uncovered line)
1
#include "git-compat-util.h"
2
#include "json-writer.h"
3
4
void jw_init(struct json_writer *jw)
5
0
{
6
0
  struct json_writer blank = JSON_WRITER_INIT;
7
0
  memcpy(jw, &blank, sizeof(*jw));;
8
0
}
9
10
void jw_release(struct json_writer *jw)
11
0
{
12
0
  strbuf_release(&jw->json);
13
0
  strbuf_release(&jw->open_stack);
14
0
}
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
0
{
21
0
  unsigned char c;
22
23
0
  strbuf_addch(out, '"');
24
0
  while ((c = *in++) != '\0') {
25
0
    if (c == '"')
26
0
      strbuf_addstr(out, "\\\"");
27
0
    else if (c == '\\')
28
0
      strbuf_addstr(out, "\\\\");
29
0
    else if (c == '\n')
30
0
      strbuf_addstr(out, "\\n");
31
0
    else if (c == '\r')
32
0
      strbuf_addstr(out, "\\r");
33
0
    else if (c == '\t')
34
0
      strbuf_addstr(out, "\\t");
35
0
    else if (c == '\f')
36
0
      strbuf_addstr(out, "\\f");
37
0
    else if (c == '\b')
38
0
      strbuf_addstr(out, "\\b");
39
0
    else if (c < 0x20)
40
0
      strbuf_addf(out, "\\u%04x", c);
41
0
    else
42
0
      strbuf_addch(out, c);
43
0
  }
44
0
  strbuf_addch(out, '"');
45
0
}
46
47
static void indent_pretty(struct json_writer *jw)
48
0
{
49
0
  strbuf_addstrings(&jw->json, "  ", jw->open_stack.len);
50
0
}
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
0
{
58
0
  jw->pretty = pretty;
59
60
0
  strbuf_addch(&jw->json, ch_open);
61
62
0
  strbuf_addch(&jw->open_stack, ch_open);
63
0
  jw->need_comma = 0;
64
0
}
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
0
{
71
0
  if (!jw->open_stack.len)
72
0
    BUG("json-writer: object: missing jw_object_begin(): '%s'", key);
73
0
  if (jw->open_stack.buf[jw->open_stack.len - 1] != '{')
74
0
    BUG("json-writer: object: not in object: '%s'", key);
75
0
}
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
0
{
82
0
  if (!jw->open_stack.len)
83
0
    BUG("json-writer: array: missing jw_array_begin()");
84
0
  if (jw->open_stack.buf[jw->open_stack.len - 1] != '[')
85
0
    BUG("json-writer: array: not in array");
86
0
}
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
0
{
93
0
  if (jw->need_comma)
94
0
    strbuf_addch(&jw->json, ',');
95
0
  else
96
0
    jw->need_comma = 1;
97
0
}
98
99
static void fmt_double(struct json_writer *jw, int precision,
100
            double value)
101
0
{
102
0
  if (precision < 0) {
103
0
    strbuf_addf(&jw->json, "%f", value);
104
0
  } else {
105
0
    struct strbuf fmt = STRBUF_INIT;
106
0
    strbuf_addf(&fmt, "%%.%df", precision);
107
0
    strbuf_addf(&jw->json, fmt.buf, value);
108
0
    strbuf_release(&fmt);
109
0
  }
110
0
}
111
112
static void object_common(struct json_writer *jw, const char *key)
113
0
{
114
0
  assert_in_object(jw, key);
115
0
  maybe_add_comma(jw);
116
117
0
  if (jw->pretty) {
118
0
    strbuf_addch(&jw->json, '\n');
119
0
    indent_pretty(jw);
120
0
  }
121
122
0
  append_quoted_string(&jw->json, key);
123
0
  strbuf_addch(&jw->json, ':');
124
0
  if (jw->pretty)
125
0
    strbuf_addch(&jw->json, ' ');
126
0
}
127
128
static void array_common(struct json_writer *jw)
129
0
{
130
0
  assert_in_array(jw);
131
0
  maybe_add_comma(jw);
132
133
0
  if (jw->pretty) {
134
0
    strbuf_addch(&jw->json, '\n');
135
0
    indent_pretty(jw);
136
0
  }
137
0
}
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
0
{
145
0
  if (jw->open_stack.len)
146
0
    BUG("json-writer: object: missing jw_end(): '%s'",
147
0
        jw->json.buf);
148
0
}
149
150
void jw_object_begin(struct json_writer *jw, int pretty)
151
0
{
152
0
  begin(jw, '{', pretty);
153
0
}
154
155
void jw_object_string(struct json_writer *jw, const char *key, const char *value)
156
0
{
157
0
  object_common(jw, key);
158
0
  append_quoted_string(&jw->json, value);
159
0
}
160
161
void jw_object_intmax(struct json_writer *jw, const char *key, intmax_t value)
162
0
{
163
0
  object_common(jw, key);
164
0
  strbuf_addf(&jw->json, "%"PRIdMAX, value);
165
0
}
166
167
void jw_object_double(struct json_writer *jw, const char *key, int precision,
168
          double value)
169
0
{
170
0
  object_common(jw, key);
171
0
  fmt_double(jw, precision, value);
172
0
}
173
174
void jw_object_true(struct json_writer *jw, const char *key)
175
0
{
176
0
  object_common(jw, key);
177
0
  strbuf_addstr(&jw->json, "true");
178
0
}
179
180
void jw_object_false(struct json_writer *jw, const char *key)
181
0
{
182
0
  object_common(jw, key);
183
0
  strbuf_addstr(&jw->json, "false");
184
0
}
185
186
void jw_object_bool(struct json_writer *jw, const char *key, int value)
187
0
{
188
0
  if (value)
189
0
    jw_object_true(jw, key);
190
0
  else
191
0
    jw_object_false(jw, key);
192
0
}
193
194
void jw_object_null(struct json_writer *jw, const char *key)
195
0
{
196
0
  object_common(jw, key);
197
0
  strbuf_addstr(&jw->json, "null");
198
0
}
199
200
static void increase_indent(struct strbuf *sb,
201
          const struct json_writer *jw,
202
          int indent)
203
0
{
204
0
  int k;
205
206
0
  strbuf_reset(sb);
207
0
  for (k = 0; k < jw->json.len; k++) {
208
0
    char ch = jw->json.buf[k];
209
0
    strbuf_addch(sb, ch);
210
0
    if (ch == '\n')
211
0
      strbuf_addchars(sb, ' ', indent);
212
0
  }
213
0
}
214
215
static void kill_indent(struct strbuf *sb,
216
      const struct json_writer *jw)
217
0
{
218
0
  int k;
219
0
  int eat_it = 0;
220
221
0
  strbuf_reset(sb);
222
0
  for (k = 0; k < jw->json.len; k++) {
223
0
    char ch = jw->json.buf[k];
224
0
    if (eat_it && ch == ' ')
225
0
      continue;
226
0
    if (ch == '\n') {
227
0
      eat_it = 1;
228
0
      continue;
229
0
    }
230
0
    eat_it = 0;
231
0
    strbuf_addch(sb, ch);
232
0
  }
233
0
}
234
235
static void append_sub_jw(struct json_writer *jw,
236
        const struct json_writer *value)
237
0
{
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
0
  if (jw->pretty && jw->open_stack.len && value->pretty) {
252
0
    struct strbuf sb = STRBUF_INIT;
253
0
    increase_indent(&sb, value, jw->open_stack.len * 2);
254
0
    strbuf_addbuf(&jw->json, &sb);
255
0
    strbuf_release(&sb);
256
0
    return;
257
0
  }
258
0
  if (!jw->pretty && value->pretty) {
259
0
    struct strbuf sb = STRBUF_INIT;
260
0
    kill_indent(&sb, value);
261
0
    strbuf_addbuf(&jw->json, &sb);
262
0
    strbuf_release(&sb);
263
0
    return;
264
0
  }
265
266
0
  strbuf_addbuf(&jw->json, &value->json);
267
0
}
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
0
{
276
0
  assert_is_terminated(value);
277
278
0
  object_common(jw, key);
279
0
  append_sub_jw(jw, value);
280
0
}
281
282
void jw_object_inline_begin_object(struct json_writer *jw, const char *key)
283
0
{
284
0
  object_common(jw, key);
285
286
0
  jw_object_begin(jw, jw->pretty);
287
0
}
288
289
void jw_object_inline_begin_array(struct json_writer *jw, const char *key)
290
0
{
291
0
  object_common(jw, key);
292
293
0
  jw_array_begin(jw, jw->pretty);
294
0
}
295
296
void jw_array_begin(struct json_writer *jw, int pretty)
297
0
{
298
0
  begin(jw, '[', pretty);
299
0
}
300
301
void jw_array_string(struct json_writer *jw, const char *value)
302
0
{
303
0
  array_common(jw);
304
0
  append_quoted_string(&jw->json, value);
305
0
}
306
307
void jw_array_intmax(struct json_writer *jw, intmax_t value)
308
0
{
309
0
  array_common(jw);
310
0
  strbuf_addf(&jw->json, "%"PRIdMAX, value);
311
0
}
312
313
void jw_array_double(struct json_writer *jw, int precision, double value)
314
0
{
315
0
  array_common(jw);
316
0
  fmt_double(jw, precision, value);
317
0
}
318
319
void jw_array_true(struct json_writer *jw)
320
0
{
321
0
  array_common(jw);
322
0
  strbuf_addstr(&jw->json, "true");
323
0
}
324
325
void jw_array_false(struct json_writer *jw)
326
0
{
327
0
  array_common(jw);
328
0
  strbuf_addstr(&jw->json, "false");
329
0
}
330
331
void jw_array_bool(struct json_writer *jw, int value)
332
0
{
333
0
  if (value)
334
0
    jw_array_true(jw);
335
0
  else
336
0
    jw_array_false(jw);
337
0
}
338
339
void jw_array_null(struct json_writer *jw)
340
0
{
341
0
  array_common(jw);
342
0
  strbuf_addstr(&jw->json, "null");
343
0
}
344
345
void jw_array_sub_jw(struct json_writer *jw, const struct json_writer *value)
346
0
{
347
0
  assert_is_terminated(value);
348
349
0
  array_common(jw);
350
0
  append_sub_jw(jw, value);
351
0
}
352
353
void jw_array_argc_argv(struct json_writer *jw, int argc, const char **argv)
354
0
{
355
0
  int k;
356
357
0
  for (k = 0; k < argc; k++)
358
0
    jw_array_string(jw, argv[k]);
359
0
}
360
361
void jw_array_argv(struct json_writer *jw, const char **argv)
362
0
{
363
0
  while (*argv)
364
0
    jw_array_string(jw, *argv++);
365
0
}
366
367
void jw_array_inline_begin_object(struct json_writer *jw)
368
0
{
369
0
  array_common(jw);
370
371
0
  jw_object_begin(jw, jw->pretty);
372
0
}
373
374
void jw_array_inline_begin_array(struct json_writer *jw)
375
0
{
376
0
  array_common(jw);
377
378
0
  jw_array_begin(jw, jw->pretty);
379
0
}
380
381
int jw_is_terminated(const struct json_writer *jw)
382
0
{
383
0
  return !jw->open_stack.len;
384
0
}
385
386
void jw_end(struct json_writer *jw)
387
0
{
388
0
  char ch_open;
389
0
  int len;
390
391
0
  if (!jw->open_stack.len)
392
0
    BUG("json-writer: too many jw_end(): '%s'", jw->json.buf);
393
394
0
  len = jw->open_stack.len - 1;
395
0
  ch_open = jw->open_stack.buf[len];
396
397
0
  strbuf_setlen(&jw->open_stack, len);
398
0
  jw->need_comma = 1;
399
400
0
  if (jw->pretty) {
401
0
    strbuf_addch(&jw->json, '\n');
402
0
    indent_pretty(jw);
403
0
  }
404
405
0
  if (ch_open == '{')
406
0
    strbuf_addch(&jw->json, '}');
407
0
  else
408
0
    strbuf_addch(&jw->json, ']');
409
0
}