Coverage Report

Created: 2026-01-10 06:18

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