Coverage Report

Created: 2026-06-30 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wsutil/json_dumper.c
Line
Count
Source
1
/* json_dumper.c
2
 * Routines for serializing data as JSON.
3
 *
4
 * Copyright 2018, Peter Wu <peter@lekensteyn.nl>
5
 * Copyright (C) 2016 Jakub Zawadzki
6
 *
7
 * Wireshark - Network traffic analyzer
8
 * By Gerald Combs <gerald@wireshark.org>
9
 * Copyright 1998 Gerald Combs
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
#include "config.h"
15
0
#define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
16
17
#include <glib.h>
18
#include <string.h>
19
20
#include "json_dumper.h"
21
#include <wsutil/to_str.h>
22
#include <math.h>
23
24
#include <wsutil/array.h>
25
#include <wsutil/wslog.h>
26
27
/*
28
 * json_dumper.state[current_depth] describes a nested element:
29
 * - type: none/object/array/non-base64 value/base64 value
30
 * - has_name: Whether the object member name was set.
31
 *
32
 * (A base64 value isn't really a nested element, but that's a
33
 * convenient way of handling them, with a begin call that opens
34
 * the string with a double-quote, one or more calls to convert
35
 * raw bytes to base64 and add them to the value, and an end call
36
 * that finishes the base64 encoding, adds any remaining raw bytes
37
 * in base64 encoding, and closes the string with a double-quote.)
38
 */
39
enum json_dumper_element_type {
40
    JSON_DUMPER_TYPE_NONE = 0,
41
    JSON_DUMPER_TYPE_VALUE = 1,
42
    JSON_DUMPER_TYPE_OBJECT = 2,
43
    JSON_DUMPER_TYPE_ARRAY = 3,
44
    JSON_DUMPER_TYPE_BASE64 = 4,
45
};
46
0
#define JSON_DUMPER_TYPE(state)         ((enum json_dumper_element_type)((state) & 7))
47
0
#define JSON_DUMPER_HAS_NAME            (1 << 3)
48
49
static const char * const json_dumper_element_type_names[] = {
50
    [JSON_DUMPER_TYPE_NONE] = "none",
51
    [JSON_DUMPER_TYPE_VALUE] = "value",
52
    [JSON_DUMPER_TYPE_OBJECT] = "object",
53
    [JSON_DUMPER_TYPE_ARRAY] = "array",
54
    [JSON_DUMPER_TYPE_BASE64] = "base64"
55
};
56
0
#define NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES array_length(json_dumper_element_type_names)
57
58
0
#define JSON_DUMPER_FLAGS_ERROR     (1 << 16)   /* Output flag: an error occurred. */
59
60
enum json_dumper_change {
61
    JSON_DUMPER_BEGIN,
62
    JSON_DUMPER_END,
63
    JSON_DUMPER_SET_NAME,
64
    JSON_DUMPER_SET_VALUE,
65
    JSON_DUMPER_WRITE_BASE64,
66
    JSON_DUMPER_FINISH,
67
};
68
69
/* Internal write buffer to reduce per-character stdio call overhead. */
70
71
static inline void
72
jd_flush(json_dumper *dumper)
73
0
{
74
0
    if (dumper->buf_pos > 0) {
75
0
        if (dumper->output_file) {
76
0
            fwrite(dumper->buf, 1, dumper->buf_pos, dumper->output_file);
77
0
        }
78
0
        if (dumper->output_string) {
79
0
            g_string_append_len(dumper->output_string, dumper->buf, dumper->buf_pos);
80
0
        }
81
0
        dumper->buf_pos = 0;
82
0
    }
83
0
}
84
85
static inline void
86
jd_buf_append(json_dumper *dumper, const char *s, size_t len)
87
0
{
88
0
    while (len > 0) {
89
0
        size_t avail = JD_BUF_SIZE - dumper->buf_pos;
90
0
        if (len <= avail) {
91
0
            memcpy(dumper->buf + dumper->buf_pos, s, len);
92
0
            dumper->buf_pos += len;
93
0
            return;
94
0
        }
95
0
        memcpy(dumper->buf + dumper->buf_pos, s, avail);
96
0
        dumper->buf_pos = JD_BUF_SIZE;
97
0
        jd_flush(dumper);
98
0
        s += avail;
99
0
        len -= avail;
100
0
    }
101
0
}
102
103
/* JSON Dumper putc */
104
static inline void
105
jd_putc(json_dumper *dumper, char c)
106
0
{
107
0
    if (dumper->buf_pos >= JD_BUF_SIZE) {
108
0
        jd_flush(dumper);
109
0
    }
110
0
    dumper->buf[dumper->buf_pos++] = c;
111
0
}
112
113
/* JSON Dumper puts */
114
static inline void
115
jd_puts(json_dumper *dumper, const char *s)
116
0
{
117
0
    jd_buf_append(dumper, s, strlen(s));
118
0
}
119
120
static inline void
121
jd_puts_len(json_dumper *dumper, const char *s, size_t len)
122
0
{
123
0
    jd_buf_append(dumper, s, len);
124
0
}
125
126
static void
127
jd_vprintf(json_dumper *dumper, const char *format, va_list args)
128
0
{
129
    /* Try to format directly into the remaining buffer space */
130
0
    size_t saved_pos = dumper->buf_pos;
131
0
    size_t avail = JD_BUF_SIZE - saved_pos;
132
0
    va_list args_copy;
133
0
    va_copy(args_copy, args);
134
0
    int n = vsnprintf(dumper->buf + saved_pos, avail, format, args);
135
0
    if (n >= 0 && (size_t)n < avail) {
136
0
        dumper->buf_pos = saved_pos + n;
137
0
        va_end(args_copy);
138
0
        return;
139
0
    }
140
    /* Didn't fit - restore position (discard truncated write), flush, retry */
141
0
    dumper->buf_pos = saved_pos;
142
0
    jd_flush(dumper);
143
0
    n = vsnprintf(dumper->buf, JD_BUF_SIZE, format, args_copy);
144
0
    if (n >= 0 && (size_t)n < JD_BUF_SIZE) {
145
0
        dumper->buf_pos = n;
146
0
    } else {
147
        /* Very large format - write directly */
148
0
        if (dumper->output_file) {
149
0
            vfprintf(dumper->output_file, format, args_copy);
150
0
        }
151
0
        if (dumper->output_string) {
152
0
            g_string_append_vprintf(dumper->output_string, format, args_copy);
153
0
        }
154
0
    }
155
0
    va_end(args_copy);
156
0
}
157
158
static void
159
json_puts_string(json_dumper *dumper, const char *str, bool dot_to_underscore)
160
0
{
161
0
    if (!str) {
162
0
        jd_puts(dumper, "null");
163
0
        return;
164
0
    }
165
166
0
    static const char json_cntrl[0x20][6] = {
167
0
        "u0000", "u0001", "u0002", "u0003", "u0004", "u0005", "u0006", "u0007", "b",     "t",     "n",     "u000b", "f",     "r",     "u000e", "u000f",
168
0
        "u0010", "u0011", "u0012", "u0013", "u0014", "u0015", "u0016", "u0017", "u0018", "u0019", "u001a", "u001b", "u001c", "u001d", "u001e", "u001f"
169
0
    };
170
171
0
    jd_putc(dumper, '"');
172
0
    if (!dot_to_underscore) {
173
        /* Fast path: scan for runs of characters that need no escaping */
174
0
        const char *p = str;
175
0
        while (*p) {
176
0
            const char *run_start = p;
177
0
            while ((unsigned char)*p >= 0x20 && *p != '\\' && *p != '"' &&
178
0
                   !(*p == '/' && p > str && *(p - 1) == '<')) {
179
0
                p++;
180
0
            }
181
0
            if (p > run_start) {
182
0
                jd_puts_len(dumper, run_start, p - run_start);
183
0
            }
184
0
            if (!*p) break;
185
0
            if ((unsigned char)*p < 0x20) {
186
0
                jd_putc(dumper, '\\');
187
0
                jd_puts(dumper, json_cntrl[(unsigned char)*p]);
188
0
            } else if (*p == '/' && p > str && *(p - 1) == '<') {
189
0
                jd_puts_len(dumper, "\\/", 2);
190
0
            } else {
191
                /* '\\' or '"' */
192
0
                jd_putc(dumper, '\\');
193
0
                jd_putc(dumper, *p);
194
0
            }
195
0
            p++;
196
0
        }
197
0
    } else {
198
        /* Dot-to-underscore path: scan for runs of plain chars (not dot/backslash/quote/control/</) */
199
0
        const char *p = str;
200
0
        while (*p) {
201
0
            const char *run_start = p;
202
0
            while ((unsigned char)*p >= 0x20 && *p != '\\' && *p != '"' && *p != '.' &&
203
0
                   !(*p == '/' && p > str && *(p - 1) == '<')) {
204
0
                p++;
205
0
            }
206
0
            if (p > run_start) {
207
0
                jd_puts_len(dumper, run_start, p - run_start);
208
0
            }
209
0
            if (!*p) break;
210
0
            if ((unsigned char)*p < 0x20) {
211
0
                jd_putc(dumper, '\\');
212
0
                jd_puts(dumper, json_cntrl[(unsigned char)*p]);
213
0
            } else if (*p == '/' && p > str && *(p - 1) == '<') {
214
0
                jd_puts_len(dumper, "\\/", 2);
215
0
            } else if (*p == '\\' || *p == '"') {
216
0
                jd_putc(dumper, '\\');
217
0
                jd_putc(dumper, *p);
218
0
            } else {
219
                /* dot -> underscore */
220
0
                jd_putc(dumper, '_');
221
0
            }
222
0
            p++;
223
0
        }
224
0
    }
225
0
    jd_putc(dumper, '"');
226
0
}
227
228
static inline uint8_t
229
json_dumper_get_prev_state(json_dumper *dumper)
230
0
{
231
0
    unsigned depth = dumper->current_depth;
232
0
    return depth != 0 ? dumper->state[depth - 1] : 0;
233
0
}
234
235
static inline uint8_t
236
json_dumper_get_curr_state(json_dumper *dumper)
237
0
{
238
0
    unsigned depth = dumper->current_depth;
239
0
    return dumper->state[depth];
240
0
}
241
242
/**
243
 * Called when a programming error is encountered where the JSON manipulation
244
 * state got corrupted. This could happen when pairing the wrong begin/end
245
 * calls, when writing multiple values for the same object, etc.
246
 */
247
static void
248
json_dumper_bad(json_dumper *dumper, const char *what)
249
0
{
250
0
    dumper->flags |= JSON_DUMPER_FLAGS_ERROR;
251
0
    if ((dumper->flags & JSON_DUMPER_FLAGS_NO_DEBUG)) {
252
        /* Console output can be slow, disable log calls to speed up fuzzing. */
253
        /*
254
         * XXX - should this call abort()?  If that flag isn't set,
255
         * ws_error() wou;d call it; is there any point in continuing
256
         * to do anything if we get here when fuzzing?
257
         */
258
0
        return;
259
0
    }
260
261
0
    if (dumper->output_file) {
262
0
        jd_flush(dumper);
263
0
        fflush(dumper->output_file);
264
0
    }
265
0
    char unknown_curr_type_name[10+1];
266
0
    char unknown_prev_type_name[10+1];
267
0
    const char *curr_type_name, *prev_type_name;
268
0
    uint8_t curr_state = json_dumper_get_curr_state(dumper);
269
0
    uint8_t curr_type = JSON_DUMPER_TYPE(curr_state);
270
0
    if (curr_type < NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES) {
271
0
        curr_type_name = json_dumper_element_type_names[curr_type];
272
0
    } else {
273
0
        snprintf(unknown_curr_type_name, sizeof unknown_curr_type_name, "%u", curr_type);
274
0
        curr_type_name = unknown_curr_type_name;
275
0
    }
276
0
    if (dumper->current_depth != 0) {
277
0
        uint8_t prev_state = json_dumper_get_prev_state(dumper);
278
0
        uint8_t prev_type = JSON_DUMPER_TYPE(prev_state);
279
0
        if (prev_type < NUM_JSON_DUMPER_ELEMENT_TYPE_NAMES) {
280
0
            prev_type_name = json_dumper_element_type_names[prev_type];
281
0
        } else {
282
0
            snprintf(unknown_prev_type_name, sizeof unknown_prev_type_name, "%u", prev_type);
283
0
            prev_type_name = unknown_prev_type_name;
284
0
        }
285
0
    } else {
286
0
        prev_type_name = "(none)";
287
0
    }
288
0
    ws_error("json_dumper error: %s: current stack depth %u, current type %s, previous_type %s",
289
0
             what, dumper->current_depth, curr_type_name, prev_type_name);
290
    /* NOTREACHED */
291
0
}
292
293
static inline bool
294
json_dumper_stack_would_overflow(json_dumper *dumper)
295
0
{
296
0
    if (dumper->current_depth + 1 >= JSON_DUMPER_MAX_DEPTH) {
297
0
        json_dumper_bad(dumper, "JSON dumper stack overflow");
298
0
        return true;
299
0
    }
300
0
    return false;
301
0
}
302
303
static inline bool
304
json_dumper_stack_would_underflow(json_dumper *dumper)
305
0
{
306
0
    if (dumper->current_depth == 0) {
307
0
        json_dumper_bad(dumper, "JSON dumper stack underflow");
308
0
        return true;
309
0
    }
310
0
    return false;
311
0
}
312
313
/**
314
 * Checks that the dumper has not already had an error.  Fail, and
315
 * return false, to tell our caller not to do any more work, if it
316
 * has.
317
 */
318
static bool
319
json_dumper_check_previous_error(json_dumper *dumper)
320
0
{
321
0
    if ((dumper->flags & JSON_DUMPER_FLAGS_ERROR)) {
322
0
        json_dumper_bad(dumper, "previous corruption detected");
323
0
        return false;
324
0
    }
325
0
    return true;
326
0
}
327
328
static void
329
print_newline_indent(json_dumper *dumper, unsigned depth)
330
0
{
331
0
    if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
332
        /* Pre-built indent: newline + up to 128 levels of 2-space indent */
333
0
        static const char indent_buf[1 + 256 + 1] =
334
0
            "\n                                                                "
335
0
            "                                                                "
336
0
            "                                                                "
337
0
            "                                                                ";
338
0
        size_t indent_len = depth * 2;
339
0
        if (indent_len <= 256) {
340
0
            jd_puts_len(dumper, indent_buf, 1 + indent_len);
341
0
        } else {
342
0
            jd_putc(dumper, '\n');
343
0
            for (unsigned i = 0; i < depth; i++) {
344
0
                jd_puts_len(dumper, "  ", 2);
345
0
            }
346
0
        }
347
0
    }
348
0
}
349
350
/**
351
 * Prints commas, newlines and indentation (if necessary). Used for array
352
 * values, object names and normal values (strings, etc.).
353
 */
354
static void
355
prepare_token(json_dumper *dumper)
356
0
{
357
0
    if (dumper->current_depth == 0) {
358
        // not part of an array or object.
359
0
        return;
360
0
    }
361
0
    uint8_t prev_state = dumper->state[dumper->current_depth - 1];
362
363
    // While processing the object value, reset the key state as it is consumed.
364
0
    dumper->state[dumper->current_depth - 1] &= ~JSON_DUMPER_HAS_NAME;
365
366
0
    switch (JSON_DUMPER_TYPE(prev_state)) {
367
0
        case JSON_DUMPER_TYPE_OBJECT:
368
0
            if ((prev_state & JSON_DUMPER_HAS_NAME)) {
369
                // Object key already set, value follows. No indentation needed.
370
0
                return;
371
0
            }
372
0
            break;
373
0
        case JSON_DUMPER_TYPE_ARRAY:
374
0
            break;
375
0
        default:
376
            // Initial values do not need indentation.
377
0
            return;
378
0
    }
379
380
0
    uint8_t curr_state = json_dumper_get_curr_state(dumper);
381
0
    if (curr_state != JSON_DUMPER_TYPE_NONE) {
382
0
        jd_putc(dumper, ',');
383
0
    }
384
0
    print_newline_indent(dumper, dumper->current_depth);
385
0
}
386
387
/**
388
 * Common code to open an object/array/base64 value, printing
389
 * an opening character.
390
 *
391
 * It also makes various correctness checks.
392
 */
393
static bool
394
json_dumper_begin_nested_element(json_dumper *dumper, enum json_dumper_element_type type)
395
0
{
396
0
    if (!json_dumper_check_previous_error(dumper)) {
397
0
        return false;
398
0
    }
399
400
    /* Make sure we won't overflow the dumper stack */
401
0
    if (json_dumper_stack_would_overflow(dumper)) {
402
0
        return false;
403
0
    }
404
405
0
    prepare_token(dumper);
406
0
    switch (type) {
407
0
        case JSON_DUMPER_TYPE_OBJECT:
408
0
            jd_putc(dumper, '{');
409
0
            break;
410
0
        case JSON_DUMPER_TYPE_ARRAY:
411
0
            jd_putc(dumper, '[');
412
0
            break;
413
0
        case JSON_DUMPER_TYPE_BASE64:
414
0
            dumper->base64_state = 0;
415
0
            dumper->base64_save = 0;
416
417
0
            jd_putc(dumper, '"');
418
0
            break;
419
0
        default:
420
0
            json_dumper_bad(dumper, "beginning unknown nested element type");
421
0
            return false;
422
0
    }
423
424
0
    dumper->state[dumper->current_depth] = type;
425
    /*
426
     * Guaranteed not to overflow, as json_dumper_stack_would_overflow()
427
     * returned false.
428
     */
429
0
    ++dumper->current_depth;
430
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_NONE;
431
0
    return true;
432
0
}
433
434
/**
435
 * Common code to close an object/array/base64 value, printing a
436
 * closing character (and if necessary, it is preceded by newline
437
 * and indentation).
438
 *
439
 * It also makes various correctness checks.
440
 */
441
static bool
442
json_dumper_end_nested_element(json_dumper *dumper, enum json_dumper_element_type type)
443
0
{
444
0
    if (!json_dumper_check_previous_error(dumper)) {
445
0
        return false;
446
0
    }
447
448
0
    uint8_t prev_state = json_dumper_get_prev_state(dumper);
449
450
0
    switch (type) {
451
0
        case JSON_DUMPER_TYPE_OBJECT:
452
0
            if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
453
0
                json_dumper_bad(dumper, "ending non-object nested item type as object");
454
0
                return false;
455
0
            }
456
0
            break;
457
0
        case JSON_DUMPER_TYPE_ARRAY:
458
0
            if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_ARRAY) {
459
0
                json_dumper_bad(dumper, "ending non-array nested item type as array");
460
0
                return false;
461
0
            }
462
0
            break;
463
0
        case JSON_DUMPER_TYPE_BASE64:
464
0
            if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_BASE64) {
465
0
                json_dumper_bad(dumper, "ending non-base64 nested item type as base64");
466
0
                return false;
467
0
            }
468
0
            break;
469
0
        default:
470
0
            json_dumper_bad(dumper, "ending unknown nested element type");
471
0
            return false;
472
0
    }
473
474
0
    if (prev_state & JSON_DUMPER_HAS_NAME) {
475
0
        json_dumper_bad(dumper, "finishing object with last item having name but no value");
476
0
        return false;
477
0
    }
478
479
    /* Make sure we won't underflow the dumper stack */
480
0
    if (json_dumper_stack_would_underflow(dumper)) {
481
0
        return false;
482
0
    }
483
484
    // if the object/array was non-empty, add a newline and indentation.
485
0
    if (dumper->state[dumper->current_depth]) {
486
0
        print_newline_indent(dumper, dumper->current_depth - 1);
487
0
    }
488
489
0
    switch (type) {
490
0
        case JSON_DUMPER_TYPE_OBJECT:
491
0
            jd_putc(dumper, '}');
492
0
            break;
493
0
        case JSON_DUMPER_TYPE_ARRAY:
494
0
            jd_putc(dumper, ']');
495
0
            break;
496
0
        case JSON_DUMPER_TYPE_BASE64:
497
0
        {
498
0
            char buf[4];
499
0
            size_t wrote;
500
501
0
            wrote = g_base64_encode_close(false, buf, &dumper->base64_state, &dumper->base64_save);
502
0
            jd_puts_len(dumper, buf, wrote);
503
504
0
            jd_putc(dumper, '"');
505
0
            break;
506
0
        }
507
0
        default:
508
0
            json_dumper_bad(dumper, "ending unknown nested element type");
509
0
            return false;
510
0
    }
511
512
    /*
513
     * Guaranteed not to underflow, as json_dumper_stack_would_underflow()
514
     * returned false.
515
     */
516
0
    --dumper->current_depth;
517
0
    return true;
518
0
}
519
520
void
521
json_dumper_begin_object(json_dumper *dumper)
522
0
{
523
0
    json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_OBJECT);
524
0
}
525
526
void
527
json_dumper_set_member_name(json_dumper *dumper, const char *name)
528
0
{
529
0
    if (!json_dumper_check_previous_error(dumper)) {
530
0
        return;
531
0
    }
532
533
0
    uint8_t prev_state = json_dumper_get_prev_state(dumper);
534
535
    /* Only object members, not array members, have names. */
536
0
    if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
537
0
        json_dumper_bad(dumper, "setting name on non-object nested item type");
538
0
        return;
539
0
    }
540
    /* An object member name can only be set once before its value is set. */
541
0
    if (prev_state & JSON_DUMPER_HAS_NAME) {
542
0
        json_dumper_bad(dumper, "setting name twice on an object member");
543
0
        return;
544
0
    }
545
546
0
    prepare_token(dumper);
547
0
    json_puts_string(dumper, name, dumper->flags & JSON_DUMPER_DOT_TO_UNDERSCORE);
548
0
    jd_putc(dumper, ':');
549
0
    if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
550
0
        jd_putc(dumper, ' ');
551
0
    }
552
553
0
    dumper->state[dumper->current_depth - 1] |= JSON_DUMPER_HAS_NAME;
554
0
}
555
556
void
557
json_dumper_set_member_name_noesc(json_dumper *dumper, const char *name, size_t len)
558
0
{
559
0
    if (!json_dumper_check_previous_error(dumper)) {
560
0
        return;
561
0
    }
562
563
0
    uint8_t prev_state = json_dumper_get_prev_state(dumper);
564
565
0
    if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_OBJECT) {
566
0
        json_dumper_bad(dumper, "setting name on non-object nested item type");
567
0
        return;
568
0
    }
569
0
    if (prev_state & JSON_DUMPER_HAS_NAME) {
570
0
        json_dumper_bad(dumper, "setting name twice on an object member");
571
0
        return;
572
0
    }
573
574
0
    prepare_token(dumper);
575
0
    jd_putc(dumper, '"');
576
0
    jd_puts_len(dumper, name, len);
577
0
    jd_putc(dumper, '"');
578
0
    jd_putc(dumper, ':');
579
0
    if ((dumper->flags & JSON_DUMPER_FLAGS_PRETTY_PRINT)) {
580
0
        jd_putc(dumper, ' ');
581
0
    }
582
583
0
    dumper->state[dumper->current_depth - 1] |= JSON_DUMPER_HAS_NAME;
584
0
}
585
586
void
587
json_dumper_end_object(json_dumper *dumper)
588
0
{
589
0
    json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_OBJECT);
590
0
}
591
592
void
593
json_dumper_begin_array(json_dumper *dumper)
594
0
{
595
0
    json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_ARRAY);
596
0
}
597
598
void
599
json_dumper_end_array(json_dumper *dumper)
600
0
{
601
0
    json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_ARRAY);
602
0
}
603
604
static bool
605
json_dumper_setting_value_ok(json_dumper *dumper)
606
0
{
607
0
    uint8_t prev_state = json_dumper_get_prev_state(dumper);
608
609
0
    switch (JSON_DUMPER_TYPE(prev_state)) {
610
0
        case JSON_DUMPER_TYPE_OBJECT:
611
            /*
612
             * This value is part of an object.  As such, it must
613
             * have a name.
614
             */
615
0
            if (!(prev_state & JSON_DUMPER_HAS_NAME)) {
616
0
                json_dumper_bad(dumper, "setting value of object member without a name");
617
0
                return false;
618
0
            }
619
0
            break;
620
0
        case JSON_DUMPER_TYPE_ARRAY:
621
            /*
622
             * This value is part of an array.  As such, it's not
623
             * required to have a name (and shouldn't have a name;
624
             * that's already been checked in json_dumper_set_member_name()).
625
             */
626
0
            break;
627
0
        case JSON_DUMPER_TYPE_BASE64:
628
            /*
629
             * We're in the middle of constructing a base64-encoded
630
             * value.  Only json_dumper_write_base64() can be used
631
             * for that; we can't add individual values to it.
632
             */
633
0
            json_dumper_bad(dumper, "attempt to set value of base64 item to something not base64-encoded");
634
0
            return false;
635
0
        case JSON_DUMPER_TYPE_NONE:
636
0
        case JSON_DUMPER_TYPE_VALUE:
637
0
        {
638
0
            uint8_t curr_state = json_dumper_get_curr_state(dumper);
639
0
            switch (JSON_DUMPER_TYPE(curr_state)) {
640
0
                case JSON_DUMPER_TYPE_NONE:
641
                    /*
642
                     * We haven't put a value yet, so we can put one now.
643
                     */
644
0
                    break;
645
0
                case JSON_DUMPER_TYPE_VALUE:
646
                    /*
647
                     * This value isn't part of an object or array,
648
                     * and we've already put one value.
649
                     */
650
0
                    json_dumper_bad(dumper, "value not in object or array immediately follows another value");
651
0
                    return false;
652
0
                case JSON_DUMPER_TYPE_OBJECT:
653
0
                case JSON_DUMPER_TYPE_ARRAY:
654
0
                case JSON_DUMPER_TYPE_BASE64:
655
                    /*
656
                     * This should never be the case, no matter what
657
                     * our callers do:
658
                     *
659
                     *    JSON_DUMPER_TYPE_OBJECT can be the previous
660
                     *    type, meaning we're in the process of adding
661
                     *    elements to an object, but it should never be
662
                     *    the current type;
663
                     *
664
                     *    JSON_DUMPER_TYPE_ARRAY can be the previous
665
                     *    type, meaning we're in the process of adding
666
                     *    elements to an array, but it should never be
667
                     *    the current type;
668
                     *
669
                     *    JSON_DUMPER_TYPE_BASE64 should only be the
670
                     *    current type if we're in the middle of
671
                     *    building a base64 value, in which case the
672
                     *    previous type should also be JSON_DUMPER_TYPE_BASE64,
673
                     *    but that's not the previous type.
674
                     */
675
0
                    json_dumper_bad(dumper, "internal error setting value - should not happen");
676
0
                    return false;
677
0
                default:
678
0
                    json_dumper_bad(dumper, "internal error setting value, bad current state - should not happen");
679
0
                    return false;
680
0
            }
681
0
            break;
682
0
        }
683
0
        default:
684
0
            json_dumper_bad(dumper, "internal error setting value, bad previous state - should not happen");
685
0
            return false;
686
0
    }
687
0
    return true;
688
0
}
689
690
void
691
json_dumper_value_string(json_dumper *dumper, const char *value)
692
0
{
693
0
    if (!json_dumper_check_previous_error(dumper)) {
694
0
        return;
695
0
    }
696
0
    if (!json_dumper_setting_value_ok(dumper)) {
697
0
        return;
698
0
    }
699
700
0
    prepare_token(dumper);
701
0
    json_puts_string(dumper, value, false);
702
703
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
704
0
}
705
706
void
707
json_dumper_value_string_noesc(json_dumper *dumper, const char *value, size_t len)
708
0
{
709
0
    if (!json_dumper_check_previous_error(dumper)) {
710
0
        return;
711
0
    }
712
0
    if (!json_dumper_setting_value_ok(dumper)) {
713
0
        return;
714
0
    }
715
716
0
    prepare_token(dumper);
717
0
    jd_putc(dumper, '"');
718
0
    jd_puts_len(dumper, value, len);
719
0
    jd_putc(dumper, '"');
720
721
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
722
0
}
723
724
void
725
json_dumper_value_double(json_dumper *dumper, double value)
726
0
{
727
0
    if (!json_dumper_check_previous_error(dumper)) {
728
0
        return;
729
0
    }
730
731
0
    if (!json_dumper_setting_value_ok(dumper)) {
732
0
        return;
733
0
    }
734
735
0
    prepare_token(dumper);
736
0
    char buffer[G_ASCII_DTOSTR_BUF_SIZE] = { 0 };
737
0
    if (isfinite(value) && g_ascii_dtostr(buffer, G_ASCII_DTOSTR_BUF_SIZE, value) && buffer[0]) {
738
0
        jd_puts(dumper, buffer);
739
0
    } else {
740
0
        jd_puts(dumper, "null");
741
0
    }
742
743
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
744
0
}
745
746
void
747
json_dumper_value_va_list(json_dumper *dumper, const char *format, va_list ap)
748
0
{
749
0
    if (!json_dumper_check_previous_error(dumper)) {
750
0
        return;
751
0
    }
752
753
0
    if (!json_dumper_setting_value_ok(dumper)) {
754
0
        return;
755
0
    }
756
757
0
    prepare_token(dumper);
758
0
    jd_vprintf(dumper, format, ap);
759
760
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
761
0
}
762
763
void
764
json_dumper_value_anyf(json_dumper *dumper, const char *format, ...)
765
0
{
766
0
    va_list ap;
767
768
0
    va_start(ap, format);
769
0
    json_dumper_value_va_list(dumper, format, ap);
770
0
    va_end(ap);
771
0
}
772
773
void
774
json_dumper_value_literal(json_dumper *dumper, const char *literal, size_t len)
775
0
{
776
0
    if (!json_dumper_check_previous_error(dumper)) {
777
0
        return;
778
0
    }
779
0
    if (!json_dumper_setting_value_ok(dumper)) {
780
0
        return;
781
0
    }
782
0
    prepare_token(dumper);
783
0
    jd_puts_len(dumper, literal, len);
784
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
785
0
}
786
787
void
788
json_dumper_value_int(json_dumper *dumper, int64_t value)
789
0
{
790
0
    if (!json_dumper_check_previous_error(dumper)) {
791
0
        return;
792
0
    }
793
0
    if (!json_dumper_setting_value_ok(dumper)) {
794
0
        return;
795
0
    }
796
0
    prepare_token(dumper);
797
798
0
    char buf[22]; /* -9223372036854775808\0 = 21 chars + extra */
799
0
    char *end = buf + sizeof(buf);
800
0
    char *p = int64_to_str_back(end, value);
801
0
    jd_puts_len(dumper, p, end - p);
802
803
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
804
0
}
805
806
void
807
json_dumper_value_uint(json_dumper *dumper, uint64_t value)
808
0
{
809
0
    if (!json_dumper_check_previous_error(dumper)) {
810
0
        return;
811
0
    }
812
0
    if (!json_dumper_setting_value_ok(dumper)) {
813
0
        return;
814
0
    }
815
0
    prepare_token(dumper);
816
817
0
    char buf[21];
818
0
    char *end = buf + sizeof(buf);
819
0
    char *p = uint64_to_str_back(end, value);
820
0
    jd_puts_len(dumper, p, end - p);
821
822
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_VALUE;
823
0
}
824
825
bool
826
json_dumper_finish(json_dumper *dumper)
827
0
{
828
0
    if (!json_dumper_check_previous_error(dumper)) {
829
0
        return false;
830
0
    }
831
832
0
    if (dumper->current_depth != 0) {
833
0
        json_dumper_bad(dumper, "JSON dumper stack not empty at finish");
834
0
        return false;
835
0
    }
836
837
0
    jd_putc(dumper, '\n');
838
0
    jd_flush(dumper);
839
0
    dumper->state[0] = JSON_DUMPER_TYPE_NONE;
840
0
    return true;
841
0
}
842
843
void
844
json_dumper_begin_base64(json_dumper *dumper)
845
0
{
846
0
    json_dumper_begin_nested_element(dumper, JSON_DUMPER_TYPE_BASE64);
847
0
}
848
849
void
850
json_dumper_write_base64(json_dumper* dumper, const unsigned char *data, size_t len)
851
0
{
852
0
    if (!json_dumper_check_previous_error(dumper)) {
853
0
        return;
854
0
    }
855
856
0
    uint8_t prev_state = json_dumper_get_prev_state(dumper);
857
858
0
    if (JSON_DUMPER_TYPE(prev_state) != JSON_DUMPER_TYPE_BASE64) {
859
0
        json_dumper_bad(dumper, "writing base64 data to a non-base64 value");
860
0
        return;
861
0
    }
862
863
0
    #define CHUNK_SIZE 1024
864
0
    char buf[(CHUNK_SIZE / 3 + 1) * 4 + 4];
865
866
0
    while (len > 0) {
867
0
        size_t chunk_size = len < CHUNK_SIZE ? len : CHUNK_SIZE;
868
0
        size_t output_size = g_base64_encode_step(data, chunk_size, false, buf, &dumper->base64_state, &dumper->base64_save);
869
0
        jd_puts_len(dumper, buf, output_size);
870
0
        data += chunk_size;
871
0
        len -= chunk_size;
872
0
    }
873
874
0
    dumper->state[dumper->current_depth] = JSON_DUMPER_TYPE_BASE64;
875
0
}
876
877
void
878
json_dumper_end_base64(json_dumper *dumper)
879
0
{
880
0
    json_dumper_end_nested_element(dumper, JSON_DUMPER_TYPE_BASE64);
881
0
}