Coverage Report

Created: 2025-07-09 06:19

/src/jansson/src/dump.c
Line
Count
Source (jump to first uncovered line)
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.53M
#define MAX_INTEGER_STR_LENGTH 25
27
1.60M
#define MAX_REAL_STR_LENGTH    25
28
29
4.23M
#define FLAGS_TO_INDENT(f)    ((f) & 0x1F)
30
1.60M
#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
9.80M
static int dump_to_strbuffer(const char *buffer, size_t size, void *data) {
39
9.80M
    return strbuffer_append_bytes((strbuffer_t *)data, buffer, size);
40
9.80M
}
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
4.18M
                       void *data) {
73
4.18M
    if (FLAGS_TO_INDENT(flags) > 0) {
74
45.5k
        unsigned int ws_count = FLAGS_TO_INDENT(flags), n_spaces = depth * ws_count;
75
76
45.5k
        if (dump("\n", 1, data))
77
0
            return -1;
78
79
13.0M
        while (n_spaces > 0) {
80
12.9M
            int cur_n =
81
12.9M
                n_spaces < sizeof whitespace - 1 ? n_spaces : sizeof whitespace - 1;
82
83
12.9M
            if (dump(whitespace, cur_n, data))
84
0
                return -1;
85
86
12.9M
            n_spaces -= cur_n;
87
12.9M
        }
88
4.14M
    } else if (space && !(flags & JSON_COMPACT)) {
89
2.72M
        return dump(" ", 1, data);
90
2.72M
    }
91
1.46M
    return 0;
92
4.18M
}
93
94
static int dump_string(const char *str, size_t len, json_dump_callback_t dump, void *data,
95
16.4k
                       size_t flags) {
96
16.4k
    const char *pos, *end, *lim;
97
16.4k
    int32_t codepoint = 0;
98
99
16.4k
    if (dump("\"", 1, data))
100
0
        return -1;
101
102
16.4k
    end = pos = str;
103
16.4k
    lim = str + len;
104
48.5k
    while (1) {
105
48.5k
        const char *text;
106
48.5k
        char seq[13];
107
48.5k
        int length;
108
109
11.2M
        while (end < lim) {
110
11.2M
            end = utf8_iterate(pos, lim - pos, &codepoint);
111
11.2M
            if (!end)
112
0
                return -1;
113
114
            /* mandatory escape or control char */
115
11.2M
            if (codepoint == '\\' || codepoint == '"' || codepoint < 0x20)
116
12.4k
                break;
117
118
            /* slash */
119
11.1M
            if ((flags & JSON_ESCAPE_SLASH) && codepoint == '/')
120
15.8k
                break;
121
122
            /* non-ASCII */
123
11.1M
            if ((flags & JSON_ENSURE_ASCII) && codepoint > 0x7F)
124
3.87k
                break;
125
126
11.1M
            pos = end;
127
11.1M
        }
128
129
48.5k
        if (pos != str) {
130
28.2k
            if (dump(str, pos - str, data))
131
0
                return -1;
132
28.2k
        }
133
134
48.5k
        if (end == pos)
135
16.4k
            break;
136
137
        /* handle \, /, ", and control codes */
138
32.0k
        length = 2;
139
32.0k
        switch (codepoint) {
140
419
            case '\\':
141
419
                text = "\\\\";
142
419
                break;
143
601
            case '\"':
144
601
                text = "\\\"";
145
601
                break;
146
2.40k
            case '\b':
147
2.40k
                text = "\\b";
148
2.40k
                break;
149
1.29k
            case '\f':
150
1.29k
                text = "\\f";
151
1.29k
                break;
152
2.24k
            case '\n':
153
2.24k
                text = "\\n";
154
2.24k
                break;
155
1.68k
            case '\r':
156
1.68k
                text = "\\r";
157
1.68k
                break;
158
3.26k
            case '\t':
159
3.26k
                text = "\\t";
160
3.26k
                break;
161
15.8k
            case '/':
162
15.8k
                text = "\\/";
163
15.8k
                break;
164
4.36k
            default: {
165
                /* codepoint is in BMP */
166
4.36k
                if (codepoint < 0x10000) {
167
2.84k
                    snprintf(seq, sizeof(seq), "\\u%04X", (unsigned int)codepoint);
168
2.84k
                    length = 6;
169
2.84k
                }
170
171
                /* not in BMP -> construct a UTF-16 surrogate pair */
172
1.52k
                else {
173
1.52k
                    int32_t first, last;
174
175
1.52k
                    codepoint -= 0x10000;
176
1.52k
                    first = 0xD800 | ((codepoint & 0xffc00) >> 10);
177
1.52k
                    last = 0xDC00 | (codepoint & 0x003ff);
178
179
1.52k
                    snprintf(seq, sizeof(seq), "\\u%04X\\u%04X", (unsigned int)first,
180
1.52k
                             (unsigned int)last);
181
1.52k
                    length = 12;
182
1.52k
                }
183
184
4.36k
                text = seq;
185
4.36k
                break;
186
0
            }
187
32.0k
        }
188
189
32.0k
        if (dump(text, length, data))
190
0
            return -1;
191
192
32.0k
        str = pos = end;
193
32.0k
    }
194
195
16.4k
    return dump("\"", 1, data);
196
16.4k
}
197
198
struct key_len {
199
    const char *key;
200
    int len;
201
};
202
203
5.12k
static int compare_keys(const void *key1, const void *key2) {
204
5.12k
    const struct key_len *k1 = key1;
205
5.12k
    const struct key_len *k2 = key2;
206
5.12k
    const size_t min_size = k1->len < k2->len ? k1->len : k2->len;
207
5.12k
    int res = memcmp(k1->key, k2->key, min_size);
208
209
5.12k
    if (res)
210
4.46k
        return res;
211
212
663
    return k1->len - k2->len;
213
5.12k
}
214
215
static int do_dump(const json_t *json, size_t flags, int depth, hashtable_t *parents,
216
3.54M
                   json_dump_callback_t dump, void *data) {
217
3.54M
    int embed = flags & JSON_EMBED;
218
219
3.54M
    flags &= ~JSON_EMBED;
220
221
3.54M
    if (!json)
222
0
        return -1;
223
224
3.54M
    switch (json_typeof(json)) {
225
1.80k
        case JSON_NULL:
226
1.80k
            return dump("null", 4, data);
227
228
747
        case JSON_TRUE:
229
747
            return dump("true", 4, data);
230
231
8.85k
        case JSON_FALSE:
232
8.85k
            return dump("false", 5, data);
233
234
1.26M
        case JSON_INTEGER: {
235
1.26M
            char buffer[MAX_INTEGER_STR_LENGTH];
236
1.26M
            int size;
237
238
1.26M
            size = snprintf(buffer, MAX_INTEGER_STR_LENGTH, "%" JSON_INTEGER_FORMAT,
239
1.26M
                            json_integer_value(json));
240
1.26M
            if (size < 0 || size >= MAX_INTEGER_STR_LENGTH)
241
0
                return -1;
242
243
1.26M
            return dump(buffer, size, data);
244
1.26M
        }
245
246
1.60M
        case JSON_REAL: {
247
1.60M
            char buffer[MAX_REAL_STR_LENGTH];
248
1.60M
            int size;
249
1.60M
            double value = json_real_value(json);
250
251
1.60M
            size = jsonp_dtostr(buffer, MAX_REAL_STR_LENGTH, value,
252
1.60M
                                FLAGS_TO_PRECISION(flags));
253
1.60M
            if (size < 0)
254
679
                return -1;
255
256
1.60M
            return dump(buffer, size, data);
257
1.60M
        }
258
259
2.07k
        case JSON_STRING:
260
2.07k
            return dump_string(json_string_value(json), json_string_length(json), dump,
261
2.07k
                               data, flags);
262
263
652k
        case JSON_ARRAY: {
264
652k
            size_t n;
265
652k
            size_t i;
266
            /* Space for "0x", double the sizeof a pointer for the hex and a
267
             * terminator. */
268
652k
            char key[2 + (sizeof(json) * 2) + 1];
269
652k
            size_t key_len;
270
271
            /* detect circular references */
272
652k
            if (jsonp_loop_check(parents, json, key, sizeof(key), &key_len))
273
0
                return -1;
274
275
652k
            n = json_array_size(json);
276
277
652k
            if (!embed && dump("[", 1, data))
278
0
                return -1;
279
652k
            if (n == 0) {
280
4.23k
                hashtable_del(parents, key, key_len);
281
4.23k
                return embed ? 0 : dump("]", 1, data);
282
4.23k
            }
283
648k
            if (dump_indent(flags, depth + 1, 0, dump, data))
284
0
                return -1;
285
286
3.52M
            for (i = 0; i < n - 1; ++i) {
287
2.87M
                if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump,
288
2.87M
                            data))
289
213
                    return -1;
290
291
2.87M
                if (dump(",", 1, data) || dump_indent(flags, depth + 1, 1, dump, data))
292
0
                    return -1;
293
2.87M
            }
294
295
648k
            if (do_dump(json_array_get(json, i), flags, depth + 1, parents, dump, data))
296
892
                return -1;
297
647k
            if (dump_indent(flags, depth, 0, dump, data))
298
0
                return -1;
299
300
647k
            hashtable_del(parents, key, key_len);
301
647k
            return embed ? 0 : dump("]", 1, data);
302
647k
        }
303
304
5.78k
        case JSON_OBJECT: {
305
5.78k
            void *iter;
306
5.78k
            const char *separator;
307
5.78k
            int separator_length;
308
5.78k
            char loop_key[LOOP_KEY_LEN];
309
5.78k
            size_t loop_key_len;
310
311
5.78k
            if (flags & JSON_COMPACT) {
312
1.78k
                separator = ":";
313
1.78k
                separator_length = 1;
314
3.99k
            } else {
315
3.99k
                separator = ": ";
316
3.99k
                separator_length = 2;
317
3.99k
            }
318
319
            /* detect circular references */
320
5.78k
            if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key),
321
5.78k
                                 &loop_key_len))
322
0
                return -1;
323
324
5.78k
            if (!embed && dump("{", 1, data))
325
0
                return -1;
326
327
5.78k
            iter = json_object_iter((json_t *)json);
328
5.78k
            if (!iter) {
329
2.09k
                hashtable_del(parents, loop_key, loop_key_len);
330
2.09k
                return embed ? 0 : dump("}", 1, data);
331
2.09k
            }
332
3.68k
            if (dump_indent(flags, depth + 1, 0, dump, data))
333
0
                return -1;
334
335
3.68k
            if (flags & JSON_SORT_KEYS) {
336
1.60k
                struct key_len *keys;
337
1.60k
                size_t size, i;
338
339
1.60k
                size = json_object_size(json);
340
1.60k
                keys = jsonp_malloc(size * sizeof(struct key_len));
341
1.60k
                if (!keys)
342
0
                    return -1;
343
344
1.60k
                i = 0;
345
5.36k
                while (iter) {
346
3.75k
                    struct key_len *keylen = &keys[i];
347
348
3.75k
                    keylen->key = json_object_iter_key(iter);
349
3.75k
                    keylen->len = json_object_iter_key_len(iter);
350
351
3.75k
                    iter = json_object_iter_next((json_t *)json, iter);
352
3.75k
                    i++;
353
3.75k
                }
354
1.60k
                assert(i == size);
355
356
1.60k
                qsort(keys, size, sizeof(struct key_len), compare_keys);
357
358
4.88k
                for (i = 0; i < size; i++) {
359
3.71k
                    const struct key_len *key;
360
3.71k
                    json_t *value;
361
362
3.71k
                    key = &keys[i];
363
3.71k
                    value = json_object_getn(json, key->key, key->len);
364
3.71k
                    assert(value);
365
366
3.71k
                    dump_string(key->key, key->len, dump, data, flags);
367
3.71k
                    if (dump(separator, separator_length, data) ||
368
3.71k
                        do_dump(value, flags, depth + 1, parents, dump, data)) {
369
432
                        jsonp_free(keys);
370
432
                        return -1;
371
432
                    }
372
373
3.27k
                    if (i < size - 1) {
374
2.10k
                        if (dump(",", 1, data) ||
375
2.10k
                            dump_indent(flags, depth + 1, 1, dump, data)) {
376
0
                            jsonp_free(keys);
377
0
                            return -1;
378
0
                        }
379
2.10k
                    } else {
380
1.17k
                        if (dump_indent(flags, depth, 0, dump, data)) {
381
0
                            jsonp_free(keys);
382
0
                            return -1;
383
0
                        }
384
1.17k
                    }
385
3.27k
                }
386
387
1.17k
                jsonp_free(keys);
388
2.07k
            } else {
389
                /* Don't sort keys */
390
391
12.4k
                while (iter) {
392
10.7k
                    void *next = json_object_iter_next((json_t *)json, iter);
393
10.7k
                    const char *key = json_object_iter_key(iter);
394
10.7k
                    const size_t key_len = json_object_iter_key_len(iter);
395
396
10.7k
                    dump_string(key, key_len, dump, data, flags);
397
10.7k
                    if (dump(separator, separator_length, data) ||
398
10.7k
                        do_dump(json_object_iter_value(iter), flags, depth + 1, parents,
399
10.7k
                                dump, data))
400
344
                        return -1;
401
402
10.3k
                    if (next) {
403
8.62k
                        if (dump(",", 1, data) ||
404
8.62k
                            dump_indent(flags, depth + 1, 1, dump, data))
405
0
                            return -1;
406
8.62k
                    } else {
407
1.73k
                        if (dump_indent(flags, depth, 0, dump, data))
408
0
                            return -1;
409
1.73k
                    }
410
411
10.3k
                    iter = next;
412
10.3k
                }
413
2.07k
            }
414
415
2.91k
            hashtable_del(parents, loop_key, loop_key_len);
416
2.91k
            return embed ? 0 : dump("}", 1, data);
417
3.68k
        }
418
419
0
        default:
420
            /* not reached */
421
0
            return -1;
422
3.54M
    }
423
3.54M
}
424
425
2.86k
char *json_dumps(const json_t *json, size_t flags) {
426
2.86k
    strbuffer_t strbuff;
427
2.86k
    char *result;
428
429
2.86k
    if (strbuffer_init(&strbuff))
430
0
        return NULL;
431
432
2.86k
    if (json_dump_callback(json, dump_to_strbuffer, (void *)&strbuff, flags))
433
330
        result = NULL;
434
2.53k
    else
435
2.53k
        result = jsonp_strdup(strbuffer_value(&strbuff));
436
437
2.86k
    strbuffer_close(&strbuff);
438
2.86k
    return result;
439
2.86k
}
440
441
0
size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags) {
442
0
    struct buffer buf = {size, 0, buffer};
443
444
0
    if (json_dump_callback(json, dump_to_buffer, (void *)&buf, flags))
445
0
        return 0;
446
447
0
    return buf.used;
448
0
}
449
450
0
int json_dumpf(const json_t *json, FILE *output, size_t flags) {
451
0
    return json_dump_callback(json, dump_to_file, (void *)output, flags);
452
0
}
453
454
0
int json_dumpfd(const json_t *json, int output, size_t flags) {
455
0
    return json_dump_callback(json, dump_to_fd, (void *)&output, flags);
456
0
}
457
458
0
int json_dump_file(const json_t *json, const char *path, size_t flags) {
459
0
    int result;
460
461
0
    FILE *output = fopen(path, "w");
462
0
    if (!output)
463
0
        return -1;
464
465
0
    result = json_dumpf(json, output, flags);
466
467
0
    if (fclose(output) != 0)
468
0
        return -1;
469
470
0
    return result;
471
0
}
472
473
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data,
474
5.73k
                       size_t flags) {
475
5.73k
    int res;
476
5.73k
    hashtable_t parents_set;
477
478
5.73k
    if (!(flags & JSON_ENCODE_ANY)) {
479
972
        if (!json_is_array(json) && !json_is_object(json))
480
64
            return -1;
481
972
    }
482
483
5.66k
    if (hashtable_init(&parents_set))
484
0
        return -1;
485
5.66k
    res = do_dump(json, flags, 0, &parents_set, callback, data);
486
5.66k
    hashtable_close(&parents_set);
487
488
5.66k
    return res;
489
5.66k
}