Coverage Report

Created: 2026-01-10 06:31

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