Coverage Report

Created: 2026-08-13 06:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/jansson/src/dump.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2009-2016 Petri Lehtinen <petri@digip.org>
3
 *
4
 * Jansson is free software; you can redistribute it and/or modify
5
 * it under the terms of the MIT license. See LICENSE for details.
6
 */
7
8
#ifndef _GNU_SOURCE
9
#define _GNU_SOURCE
10
#endif
11
12
#include "jansson_private.h"
13
14
#include <assert.h>
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>
18
#ifdef HAVE_UNISTD_H
19
#include <unistd.h>
20
#endif
21
22
#include "jansson.h"
23
#include "strbuffer.h"
24
#include "utf.h"
25
26
2.32M
#define MAX_INTEGER_STR_LENGTH 25
27
939k
#define MAX_REAL_STR_LENGTH    25
28
29
2.31M
#define FLAGS_TO_INDENT(f)    ((f) & 0x1F)
30
939k
#define FLAGS_TO_PRECISION(f) (((f) >> 11) & 0x1F)
31
32
struct buffer {
33
    const size_t size;
34
    size_t used;
35
    char *data;
36
};
37
38
7.14M
static int dump_to_strbuffer(const char *buffer, size_t size, void *data) {
39
7.14M
    return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
40
7.14M
}
41
42
0
static int dump_to_buffer(const char *buffer, size_t size, void *data) {
43
0
    struct buffer *buf = (struct buffer *)data;
44
45
0
    if (buf->used + size <= buf->size)
46
0
        memcpy(&buf->data[buf->used], buffer, size);
47
48
0
    buf->used += size;
49
0
    return 0;
50
0
}
51
52
0
static int dump_to_file(const char *buffer, size_t size, void *data) {
53
0
    FILE *dest = (FILE *)data;
54
0
    if (fwrite(buffer, size, 1, dest) != 1)
55
0
        return -1;
56
0
    return 0;
57
0
}
58
59
0
static int dump_to_fd(const char *buffer, size_t size, void *data) {
60
0
#ifdef HAVE_UNISTD_H
61
0
    int *dest = (int *)data;
62
0
    if (write(*dest, buffer, size) == (ssize_t)size)
63
0
        return 0;
64
0
#endif
65
0
    return -1;
66
0
}
67
68
/* 32 spaces (the maximum indentation size) */
69
static const char whitespace[] = "                                ";
70
71
static int dump_indent(size_t flags, int depth, int space, json_dump_callback_t dump,
72
2.31M
                       void *data) {
73
2.31M
    if (FLAGS_TO_INDENT(flags) > 0) {
74
0
        unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
75
76
0
        if (dump("\n", 1, data))
77
0
            return -1;
78
79
0
        while (n_spaces > 0) {
80
0
            int cur_n =
81
0
                n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
82
83
0
            if (dump(whitespace, cur_n, data))
84
0
                return -1;
85
86
0
            n_spaces -= cur_n;
87
0
        }
88
2.31M
    } else if (space && !(flags & JSON_COMPACT)) {
89
1.45M
        return dump(" ", 1, data);
90
1.45M
    }
91
854k
    return 0;
92
2.31M
}
93
94
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data,
95
11.8k
                       size_t flags) {
96
11.8k
    const char *pos, *end, *lim;
97
11.8k
    int32_t codepoint = 0;
98
99
11.8k
    if (dump("\"", 1, data))
100
0
        return -1;
101
102
11.8k
    end = pos = str;
103
11.8k
    lim = str + len;
104
914k
    while (1) {
105
914k
        const char *text;
106
914k
        char seq[13];
107
914k
        int length;
108
109
17.7M
        while (end < lim) {
110
17.7M
            end = utf8_iterate(pos, lim - pos, &codepoint);
111
17.7M
            if (!end)
112
0
                return -1;
113
114
            /* mandatory escape or control char */
115
17.7M
            if (codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
116
23.5k
                break;
117
118
            /* slash */
119
17.7M
            if ((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
120
0
                break;
121
122
            /* non-ASCII */
123
17.7M
            if ((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
124
879k
                break;
125
126
16.8M
            pos = end;
127
16.8M
        }
128
129
914k
        if (pos != str) {
130
93.9k
            if (dump(str, pos - str, data))
131
0
                return -1;
132
93.9k
        }
133
134
914k
        if (end == pos)
135
11.8k
            break;
136
137
        /* handle \, /, ", and control codes */
138
902k
        length = 2;
139
902k
        switch (codepoint) {
140
503
            case '\\':
141
503
                text = "\\\\";
142
503
                break;
143
371
            case '\"':
144
371
                text = "\\\"";
145
371
                break;
146
433
            case '\b':
147
433
                text = "\\b";
148
433
                break;
149
3.55k
            case '\f':
150
3.55k
                text = "\\f";
151
3.55k
                break;
152
298
            case '\n':
153
298
                text = "\\n";
154
298
                break;
155
377
            case '\r':
156
377
                text = "\\r";
157
377
                break;
158
17.9k
            case '\t':
159
17.9k
                text = "\\t";
160
17.9k
                break;
161
0
            case '/':
162
0
                text = "\\/";
163
0
                break;
164
879k
            default: {
165
                /* codepoint is in BMP */
166
879k
                if (codepoint < 0x10000) {
167
874k
                    snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
168
874k
                    length = 6;
169
874k
                }
170
171
                /* not in BMP -> construct a UTF-16 surrogate pair */
172
5.16k
                else {
173
5.16k
                    int32_t first, last;
174
175
5.16k
                    codepoint -= 0x10000;
176
5.16k
                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
177
5.16k
                    last = 0xDC00 | (codepoint & 0x003ff);
178
179
5.16k
                    snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first,
180
5.16k
                             (unsigned int)last);
181
5.16k
                    length = 12;
182
5.16k
                }
183
184
879k
                text = seq;
185
879k
                break;
186
0
            }
187
902k
        }
188
189
902k
        if (dump(text, length, data))
190
0
            return -1;
191
192
902k
        str = pos = end;
193
902k
    }
194
195
11.8k
    return dump("\"", 1, data);
196
11.8k
}
197
198
struct key_len {
199
    const char *key;
200
    int len;
201
};
202
203
19.2k
static int compare_keys(const void *key1, const void *key2) {
204
19.2k
    const struct key_len *k1 = key1;
205
19.2k
    const struct key_len *k2 = key2;
206
19.2k
    const size_t min_size = k1->len < k2->len ? k1->len : k2->len;
207
19.2k
    int res = memcmp(k1->key, k2->key, min_size);
208
209
19.2k
    if (res)
210
18.0k
        return res;
211
212
1.17k
    return k1->len - k2->len;
213
19.2k
}
214
215
static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents,
216
2.27M
                   json_dump_callback_t dump, void *data) {
217
2.27M
    int embed = flags & JSON_EMBED;
218
219
2.27M
    flags &= ~JSON_EMBED;
220
221
2.27M
    if (!json)
222
0
        return -1;
223
224
2.27M
    if (depth >= JSON_PARSER_MAX_DEPTH)
225
0
        return -1;
226
227
2.27M
    switch (json_typeof(json)) {
228
28.2k
        case JSON_NULL:
229
28.2k
            return dump("null", 4, data);
230
231
429
        case JSON_TRUE:
232
429
            return dump("true", 4, data);
233
234
1.90k
        case JSON_FALSE:
235
1.90k
            return dump("false", 5, data);
236
237
1.16M
        case JSON_INTEGER: {
238
1.16M
            char buffer[MAX_INTEGER_STR_LENGTH];
239
1.16M
            int size;
240
241
1.16M
            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT,
242
1.16M
                            json_integer_value(json));
243
1.16M
            if (size < 0 || size >= MAX_INTEGER_STR_LENGTH)
244
0
                return -1;
245
246
1.16M
            return dump(buffer, size, data);
247
1.16M
        }
248
249
939k
        case JSON_REAL: {
250
939k
            char buffer[MAX_REAL_STR_LENGTH];
251
939k
            int size;
252
939k
            double value = json_real_value(json);
253
254
939k
            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
255
939k
                                FLAGS_TO_PRECISION(flags));
256
939k
            if (size < 0)
257
0
                return -1;
258
259
939k
            return dump(buffer, size, data);
260
939k
        }
261
262
1.14k
        case JSON_STRING:
263
1.14k
            return dump_string(json_string_value(json), json_string_length(json), dump,
264
1.14k
                               data, flags);
265
266
37.4k
        case JSON_ARRAY: {
267
37.4k
            size_t n;
268
37.4k
            size_t i;
269
            /* Space for "0x", double the sizeof a pointer for the hex and a
270
             * terminator. */
271
37.4k
            char key[2 + (sizeof(json) * 2) + 1];
272
37.4k
            size_t key_len;
273
274
            /* detect circular references */
275
37.4k
            if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len))
276
0
                return -1;
277
278
37.4k
            n = json_array_size(json);
279
280
37.4k
            if (!embed && dump("[", 1, data))
281
0
                return -1;
282
37.4k
            if (n == 0) {
283
476
                hashtable_del(parents, key, key_len);
284
476
                return embed ? 0 : dump("]", 1, data);
285
476
            }
286
36.9k
            if (dump_indent(flags, depth + 1, 0, dump, data))
287
0
                return -1;
288
289
2.26M
            for (i = 0; i < n - 1; ++i) {
290
2.22M
                if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump,
291
2.22M
                            data))
292
0
                    return -1;
293
294
2.22M
                if (dump(",", 1, data) || dump_indent(flags, depth + 1, 1, dump, data))
295
0
                    return -1;
296
2.22M
            }
297
298
36.9k
            if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump, data))
299
0
                return -1;
300
36.9k
            if (dump_indent(flags, depth, 0, dump, data))
301
0
                return -1;
302
303
36.9k
            hashtable_del(parents, key, key_len);
304
36.9k
            return embed ? 0 : dump("]", 1, data);
305
36.9k
        }
306
307
104k
        case JSON_OBJECT: {
308
104k
            void *iter;
309
104k
            const char *separator;
310
104k
            int separator_length;
311
104k
            char loop_key[LOOP_KEY_LEN];
312
104k
            size_t loop_key_len;
313
314
104k
            if (flags & JSON_COMPACT) {
315
1.82k
                separator = ":";
316
1.82k
                separator_length = 1;
317
102k
            } else {
318
102k
                separator = ": ";
319
102k
                separator_length = 2;
320
102k
            }
321
322
            /* detect circular references */
323
104k
            if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key),
324
104k
                                 &loop_key_len))
325
0
                return -1;
326
327
104k
            if (!embed && dump("{", 1, data))
328
0
                return -1;
329
330
104k
            iter = json_object_iter((json_t *)json);
331
104k
            if (!iter) {
332
101k
                hashtable_del(parents, loop_key, loop_key_len);
333
101k
                return embed ? 0 : dump("}", 1, data);
334
101k
            }
335
2.81k
            if (dump_indent(flags, depth + 1, 0, dump, data))
336
0
                return -1;
337
338
2.81k
            if (flags & JSON_SORT_KEYS) {
339
2.07k
                struct key_len *keys;
340
2.07k
                size_t size, i;
341
342
2.07k
                size = json_object_size(json);
343
2.07k
                keys = jsonp_malloc(size * sizeof(struct key_len));
344
2.07k
                if (!keys)
345
0
                    return -1;
346
347
2.07k
                i = 0;
348
11.1k
                while (iter) {
349
9.08k
                    struct key_len *keylen = &keys[i];
350
351
9.08k
                    keylen->key = json_object_iter_key(iter);
352
9.08k
                    keylen->len = json_object_iter_key_len(iter);
353
354
9.08k
                    iter = json_object_iter_next((json_t *)json, iter);
355
9.08k
                    i++;
356
9.08k
                }
357
2.07k
                assert(i == size);
358
359
2.07k
                qsort(keys, size, sizeof(struct key_len), compare_keys);
360
361
11.1k
                for (i = 0; i < size; i++) {
362
9.08k
                    const struct key_len *key;
363
9.08k
                    json_t *value;
364
365
9.08k
                    key = &keys[i];
366
9.08k
                    value = json_object_getn(json, key->key, key->len);
367
9.08k
                    assert(value);
368
369
9.08k
                    dump_string(key->key, key->len, dump, data, flags);
370
9.08k
                    if (dump(separator, separator_length, data) ||
371
9.08k
                        do_dump(value, flags, depth + 1, parents, dump, data)) {
372
0
                        jsonp_free(keys);
373
0
                        return -1;
374
0
                    }
375
376
9.08k
                    if (i < size - 1) {
377
7.01k
                        if (dump(",", 1, data) ||
378
7.01k
                            dump_indent(flags, depth + 1, 1, dump, data)) {
379
0
                            jsonp_free(keys);
380
0
                            return -1;
381
0
                        }
382
7.01k
                    } else {
383
2.07k
                        if (dump_indent(flags, depth, 0, dump, data)) {
384
0
                            jsonp_free(keys);
385
0
                            return -1;
386
0
                        }
387
2.07k
                    }
388
9.08k
                }
389
390
2.07k
                jsonp_free(keys);
391
2.07k
            } else {
392
                /* Don't sort keys */
393
394
2.39k
                while (iter) {
395
1.65k
                    void *next = json_object_iter_next((json_t *)json, iter);
396
1.65k
                    const char *key = json_object_iter_key(iter);
397
1.65k
                    const size_t key_len = json_object_iter_key_len(iter);
398
399
1.65k
                    dump_string(key, key_len, dump, data, flags);
400
1.65k
                    if (dump(separator, separator_length, data) ||
401
1.65k
                        do_dump(json_object_iter_value(iter), flags, depth + 1, parents,
402
1.65k
                                dump, data))
403
0
                        return -1;
404
405
1.65k
                    if (next) {
406
909
                        if (dump(",", 1, data) ||
407
909
                            dump_indent(flags, depth + 1, 1, dump, data))
408
0
                            return -1;
409
909
                    } else {
410
745
                        if (dump_indent(flags, depth, 0, dump, data))
411
0
                            return -1;
412
745
                    }
413
414
1.65k
                    iter = next;
415
1.65k
                }
416
745
            }
417
418
2.81k
            hashtable_del(parents, loop_key, loop_key_len);
419
2.81k
            return embed ? 0 : dump("}", 1, data);
420
2.81k
        }
421
422
0
        default:
423
            /* not reached */
424
0
            return -1;
425
2.27M
    }
426
2.27M
}
427
428
4.27k
char *json_dumps(const json_t *json, size_t flags) {
429
4.27k
    strbuffer_t strbuff;
430
4.27k
    char *result;
431
432
4.27k
    if (strbuffer_init(&strbuff))
433
0
        return NULL;
434
435
4.27k
    if (json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
436
105
        result = NULL;
437
4.16k
    else {
438
4.16k
        char *new_result;
439
4.16k
        result = strbuffer_steal_value(&strbuff);
440
        // technically the resizing is not needed.
441
4.16k
        new_result = jsonp_realloc(result, strbuff.size, strbuff.length + 1);
442
4.16k
        if (new_result) { // when realloc fails we just use the original pointer
443
4.16k
            result = new_result;
444
4.16k
        }
445
4.16k
    }
446
447
4.27k
    strbuffer_close(&strbuff);
448
4.27k
    return result;
449
4.27k
}
450
451
0
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) {
452
0
    struct buffer buf = {size, 0, buffer};
453
454
0
    if (json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
455
0
        return 0;
456
457
0
    return buf.used;
458
0
}
459
460
0
int json_dumpf(const json_t *json, FILE *output, size_t flags) {
461
0
    return json_dump_callback(json, dump_to_file, (void *)output, flags);
462
0
}
463
464
0
int json_dumpfd(const json_t *json, int output, size_t flags) {
465
0
    return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
466
0
}
467
468
0
int json_dump_file(const json_t *json, const char *path, size_t flags) {
469
0
    int result;
470
471
0
    FILE *output = fopen(path, "w");
472
0
    if (!output)
473
0
        return -1;
474
475
0
    result = json_dumpf(json, output, flags);
476
477
0
    if (fclose(output) != 0)
478
0
        return -1;
479
480
0
    return result;
481
0
}
482
483
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data,
484
4.27k
                       size_t flags) {
485
4.27k
    int res;
486
4.27k
    hashtable_t parents_set;
487
488
4.27k
    if (!(flags & JSON_ENCODE_ANY)) {
489
4.27k
        if (!json_is_array(json) && !json_is_object(json))
490
105
            return -1;
491
4.27k
    }
492
493
4.16k
    if (hashtable_init(&parents_set))
494
0
        return -1;
495
4.16k
    res = do_dump(json, flags, 0, &parents_set, callback, data);
496
4.16k
    hashtable_close(&parents_set);
497
498
4.16k
    return res;
499
4.16k
}