Coverage Report

Created: 2026-08-31 07:13

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
543k
#define MAX_INTEGER_STR_LENGTH 25
27
345k
#define MAX_REAL_STR_LENGTH    25
28
29
901k
#define FLAGS_TO_INDENT(f)    ((f) & 0x1F)
30
345k
#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
2.73M
static int dump_to_strbuffer(const char *buffer, size_t size, void *data) {
39
2.73M
    return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
40
2.73M
}
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
901k
                       void *data) {
73
901k
    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
901k
    } else if (space && !(flags & JSON_COMPACT)) {
89
0
        return dump(" ", 1, data);
90
0
    }
91
901k
    return 0;
92
901k
}
93
94
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data,
95
348k
                       size_t flags) {
96
348k
    const char *pos, *end, *lim;
97
348k
    int32_t codepoint = 0;
98
99
348k
    if (dump("\"", 1, data))
100
0
        return -1;
101
102
348k
    end = pos = str;
103
348k
    lim = str + len;
104
359k
    while (1) {
105
359k
        const char *text;
106
359k
        char seq[13];
107
359k
        int length;
108
109
39.5M
        while (end < lim) {
110
39.1M
            end = utf8_iterate(pos, lim - pos, &codepoint);
111
39.1M
            if (!end)
112
0
                return -1;
113
114
            /* mandatory escape or control char */
115
39.1M
            if (codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
116
11.3k
                break;
117
118
            /* slash */
119
39.1M
            if ((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
120
0
                break;
121
122
            /* non-ASCII */
123
39.1M
            if ((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
124
0
                break;
125
126
39.1M
            pos = end;
127
39.1M
        }
128
129
359k
        if (pos != str) {
130
337k
            if (dump(str, pos - str, data))
131
0
                return -1;
132
337k
        }
133
134
359k
        if (end == pos)
135
348k
            break;
136
137
        /* handle \, /, ", and control codes */
138
11.3k
        length = 2;
139
11.3k
        switch (codepoint) {
140
659
            case '\\':
141
659
                text = "\\\\";
142
659
                break;
143
430
            case '\"':
144
430
                text = "\\\"";
145
430
                break;
146
427
            case '\b':
147
427
                text = "\\b";
148
427
                break;
149
525
            case '\f':
150
525
                text = "\\f";
151
525
                break;
152
5.19k
            case '\n':
153
5.19k
                text = "\\n";
154
5.19k
                break;
155
2.93k
            case '\r':
156
2.93k
                text = "\\r";
157
2.93k
                break;
158
337
            case '\t':
159
337
                text = "\\t";
160
337
                break;
161
0
            case '/':
162
0
                text = "\\/";
163
0
                break;
164
871
            default: {
165
                /* codepoint is in BMP */
166
871
                if (codepoint < 0x10000) {
167
871
                    snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
168
871
                    length = 6;
169
871
                }
170
171
                /* not in BMP -> construct a UTF-16 surrogate pair */
172
0
                else {
173
0
                    int32_t first, last;
174
175
0
                    codepoint -= 0x10000;
176
0
                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
177
0
                    last = 0xDC00 | (codepoint & 0x003ff);
178
179
0
                    snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first,
180
0
                             (unsigned int)last);
181
0
                    length = 12;
182
0
                }
183
184
871
                text = seq;
185
871
                break;
186
0
            }
187
11.3k
        }
188
189
11.3k
        if (dump(text, length, data))
190
0
            return -1;
191
192
11.3k
        str = pos = end;
193
11.3k
    }
194
195
348k
    return dump("\"", 1, data);
196
348k
}
197
198
struct key_len {
199
    const char *key;
200
    int len;
201
};
202
203
0
static int compare_keys(const void *key1, const void *key2) {
204
0
    const struct key_len *k1 = key1;
205
0
    const struct key_len *k2 = key2;
206
0
    const size_t min_size = k1->len < k2->len ? k1->len : k2->len;
207
0
    int res = memcmp(k1->key, k2->key, min_size);
208
209
0
    if (res)
210
0
        return res;
211
212
0
    return k1->len - k2->len;
213
0
}
214
215
static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents,
216
879k
                   json_dump_callback_t dump, void *data) {
217
879k
    int embed = flags & JSON_EMBED;
218
219
879k
    flags &= ~JSON_EMBED;
220
221
879k
    if (!json)
222
0
        return -1;
223
224
879k
    if (depth >= JSON_PARSER_MAX_DEPTH)
225
0
        return -1;
226
227
879k
    switch (json_typeof(json)) {
228
1.15k
        case JSON_NULL:
229
1.15k
            return dump("null", 4, data);
230
231
6.41k
        case JSON_TRUE:
232
6.41k
            return dump("true", 4, data);
233
234
590
        case JSON_FALSE:
235
590
            return dump("false", 5, data);
236
237
271k
        case JSON_INTEGER: {
238
271k
            char buffer[MAX_INTEGER_STR_LENGTH];
239
271k
            int size;
240
241
271k
            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT,
242
271k
                            json_integer_value(json));
243
271k
            if (size < 0 || size >= MAX_INTEGER_STR_LENGTH)
244
0
                return -1;
245
246
271k
            return dump(buffer, size, data);
247
271k
        }
248
249
345k
        case JSON_REAL: {
250
345k
            char buffer[MAX_REAL_STR_LENGTH];
251
345k
            int size;
252
345k
            double value = json_real_value(json);
253
254
345k
            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
255
345k
                                FLAGS_TO_PRECISION(flags));
256
345k
            if (size < 0)
257
0
                return -1;
258
259
345k
            return dump(buffer, size, data);
260
345k
        }
261
262
185k
        case JSON_STRING:
263
185k
            return dump_string(json_string_value(json), json_string_length(json), dump,
264
185k
                               data, flags);
265
266
16.4k
        case JSON_ARRAY: {
267
16.4k
            size_t n;
268
16.4k
            size_t i;
269
            /* Space for "0x", double the sizeof a pointer for the hex and a
270
             * terminator. */
271
16.4k
            char key[2 + (sizeof(json) * 2) + 1];
272
16.4k
            size_t key_len;
273
274
            /* detect circular references */
275
16.4k
            if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len))
276
0
                return -1;
277
278
16.4k
            n = json_array_size(json);
279
280
16.4k
            if (!embed && dump("[", 1, data))
281
0
                return -1;
282
16.4k
            if (n == 0) {
283
826
                hashtable_del(parents, key, key_len);
284
826
                return embed ? 0 : dump("]", 1, data);
285
826
            }
286
15.5k
            if (dump_indent(flags, depth + 1, 0, dump, data))
287
0
                return -1;
288
289
671k
            for (i = 0; i < n - 1; ++i) {
290
655k
                if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump,
291
655k
                            data))
292
0
                    return -1;
293
294
655k
                if (dump(",", 1, data) || dump_indent(flags, depth + 1, 1, dump, data))
295
0
                    return -1;
296
655k
            }
297
298
15.5k
            if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump, data))
299
0
                return -1;
300
15.5k
            if (dump_indent(flags, depth, 0, dump, data))
301
0
                return -1;
302
303
15.5k
            hashtable_del(parents, key, key_len);
304
15.5k
            return embed ? 0 : dump("]", 1, data);
305
15.5k
        }
306
307
52.4k
        case JSON_OBJECT: {
308
52.4k
            void *iter;
309
52.4k
            const char *separator;
310
52.4k
            int separator_length;
311
52.4k
            char loop_key[LOOP_KEY_LEN];
312
52.4k
            size_t loop_key_len;
313
314
52.4k
            if (flags & JSON_COMPACT) {
315
52.4k
                separator = ":";
316
52.4k
                separator_length = 1;
317
52.4k
            } else {
318
0
                separator = ": ";
319
0
                separator_length = 2;
320
0
            }
321
322
            /* detect circular references */
323
52.4k
            if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key),
324
52.4k
                                 &loop_key_len))
325
0
                return -1;
326
327
52.4k
            if (!embed && dump("{", 1, data))
328
0
                return -1;
329
330
52.4k
            iter = json_object_iter((json_t *)json);
331
52.4k
            if (!iter) {
332
562
                hashtable_del(parents, loop_key, loop_key_len);
333
562
                return embed ? 0 : dump("}", 1, data);
334
562
            }
335
51.8k
            if (dump_indent(flags, depth + 1, 0, dump, data))
336
0
                return -1;
337
338
51.8k
            if (flags & JSON_SORT_KEYS) {
339
0
                struct key_len *keys;
340
0
                size_t size, i;
341
342
0
                size = json_object_size(json);
343
0
                keys = jsonp_malloc(size * sizeof(struct key_len));
344
0
                if (!keys)
345
0
                    return -1;
346
347
0
                i = 0;
348
0
                while (iter) {
349
0
                    struct key_len *keylen = &keys[i];
350
351
0
                    keylen->key = json_object_iter_key(iter);
352
0
                    keylen->len = json_object_iter_key_len(iter);
353
354
0
                    iter = json_object_iter_next((json_t *)json, iter);
355
0
                    i++;
356
0
                }
357
0
                assert(i == size);
358
359
0
                qsort(keys, size, sizeof(struct key_len), compare_keys);
360
361
0
                for (i = 0; i < size; i++) {
362
0
                    const struct key_len *key;
363
0
                    json_t *value;
364
365
0
                    key = &keys[i];
366
0
                    value = json_object_getn(json, key->key, key->len);
367
0
                    assert(value);
368
369
0
                    dump_string(key->key, key->len, dump, data, flags);
370
0
                    if (dump(separator, separator_length, data) ||
371
0
                        do_dump(value, flags, depth + 1, parents, dump, data)) {
372
0
                        jsonp_free(keys);
373
0
                        return -1;
374
0
                    }
375
376
0
                    if (i < size - 1) {
377
0
                        if (dump(",", 1, data) ||
378
0
                            dump_indent(flags, depth + 1, 1, dump, data)) {
379
0
                            jsonp_free(keys);
380
0
                            return -1;
381
0
                        }
382
0
                    } else {
383
0
                        if (dump_indent(flags, depth, 0, dump, data)) {
384
0
                            jsonp_free(keys);
385
0
                            return -1;
386
0
                        }
387
0
                    }
388
0
                }
389
390
0
                jsonp_free(keys);
391
51.8k
            } else {
392
                /* Don't sort keys */
393
394
214k
                while (iter) {
395
162k
                    void *next = json_object_iter_next((json_t *)json, iter);
396
162k
                    const char *key = json_object_iter_key(iter);
397
162k
                    const size_t key_len = json_object_iter_key_len(iter);
398
399
162k
                    dump_string(key, key_len, dump, data, flags);
400
162k
                    if (dump(separator, separator_length, data) ||
401
162k
                        do_dump(json_object_iter_value(iter), flags, depth + 1, parents,
402
162k
                                dump, data))
403
0
                        return -1;
404
405
162k
                    if (next) {
406
110k
                        if (dump(",", 1, data) ||
407
110k
                            dump_indent(flags, depth + 1, 1, dump, data))
408
0
                            return -1;
409
110k
                    } else {
410
51.8k
                        if (dump_indent(flags, depth, 0, dump, data))
411
0
                            return -1;
412
51.8k
                    }
413
414
162k
                    iter = next;
415
162k
                }
416
51.8k
            }
417
418
51.8k
            hashtable_del(parents, loop_key, loop_key_len);
419
51.8k
            return embed ? 0 : dump("}", 1, data);
420
51.8k
        }
421
422
0
        default:
423
            /* not reached */
424
0
            return -1;
425
879k
    }
426
879k
}
427
428
45.5k
char *json_dumps(const json_t *json, size_t flags) {
429
45.5k
    strbuffer_t strbuff;
430
45.5k
    char *result;
431
432
45.5k
    if (strbuffer_init(&strbuff))
433
0
        return NULL;
434
435
45.5k
    if (json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
436
8
        result = NULL;
437
45.5k
    else {
438
45.5k
        char *new_result;
439
45.5k
        result = strbuffer_steal_value(&strbuff);
440
        // technically the resizing is not needed.
441
45.5k
        new_result = jsonp_realloc(result, strbuff.size, strbuff.length + 1);
442
45.5k
        if (new_result) { // when realloc fails we just use the original pointer
443
45.5k
            result = new_result;
444
45.5k
        }
445
45.5k
    }
446
447
45.5k
    strbuffer_close(&strbuff);
448
45.5k
    return result;
449
45.5k
}
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
45.5k
                       size_t flags) {
485
45.5k
    int res;
486
45.5k
    hashtable_t parents_set;
487
488
45.5k
    if (!(flags & JSON_ENCODE_ANY)) {
489
16.6k
        if (!json_is_array(json) && !json_is_object(json))
490
8
            return -1;
491
16.6k
    }
492
493
45.5k
    if (hashtable_init(&parents_set))
494
0
        return -1;
495
45.5k
    res = do_dump(json, flags, 0, &parents_set, callback, data);
496
45.5k
    hashtable_close(&parents_set);
497
498
45.5k
    return res;
499
45.5k
}