Coverage Report

Created: 2026-06-09 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qpdf/libqpdf/JSON.cc
Line
Count
Source
1
#include <qpdf/JSON.hh>
2
3
#include <qpdf/JSON_writer.hh>
4
5
#include <qpdf/InputSource_private.hh>
6
#include <qpdf/Pl_Base64.hh>
7
#include <qpdf/Pl_Concatenate.hh>
8
#include <qpdf/Pl_String.hh>
9
#include <qpdf/QTC.hh>
10
#include <qpdf/QUtil.hh>
11
#include <qpdf/Util.hh>
12
13
#include <cstring>
14
#include <stdexcept>
15
16
using namespace qpdf;
17
18
JSON::Members::Members(std::unique_ptr<JSON_value> value) :
19
9.46M
    value(std::move(value))
20
9.46M
{
21
9.46M
}
22
23
JSON::JSON(std::unique_ptr<JSON_value> value) :
24
9.46M
    m(new Members(std::move(value)))
25
9.46M
{
26
9.46M
}
27
28
void
29
JSON::writeClose(Pipeline* p, bool first, size_t depth, char const* delimiter)
30
200k
{
31
200k
    if (first) {
32
23.0k
        *p << delimiter;
33
176k
    } else {
34
176k
        std::string s{"\n"};
35
176k
        s.append(2 * depth, ' ');
36
176k
        *p << s + delimiter;
37
176k
    }
38
200k
}
39
40
void
41
JSON::writeNext(Pipeline* p, bool& first, size_t depth)
42
1.90M
{
43
1.90M
    if (first) {
44
176k
        first = false;
45
176k
        std::string s{"\n"};
46
176k
        s.append(2 * depth, ' ');
47
176k
        *p << s;
48
1.72M
    } else {
49
1.72M
        std::string s{",\n"};
50
1.72M
        s.append(2 * depth, ' ');
51
1.72M
        *p << s;
52
1.72M
    }
53
1.90M
}
54
55
void
56
JSON::writeDictionaryOpen(Pipeline* p, bool& first, size_t depth)
57
113k
{
58
113k
    *p << "{";
59
113k
    first = true;
60
113k
}
61
62
void
63
JSON::writeArrayOpen(Pipeline* p, bool& first, size_t depth)
64
86.5k
{
65
86.5k
    *p << "[";
66
86.5k
    first = true;
67
86.5k
}
68
69
void
70
JSON::writeDictionaryClose(Pipeline* p, bool first, size_t depth)
71
113k
{
72
113k
    writeClose(p, first, depth, "}");
73
113k
}
74
75
void
76
JSON::writeArrayClose(Pipeline* p, bool first, size_t depth)
77
86.5k
{
78
86.5k
    writeClose(p, first, depth, "]");
79
86.5k
}
80
81
void
82
JSON::writeDictionaryKey(Pipeline* p, bool& first, std::string const& key, size_t depth)
83
430k
{
84
430k
    writeNext(p, first, depth);
85
430k
    *p << std::string("\"") + key + "\": ";
86
430k
}
87
88
void
89
JSON::writeDictionaryItem(
90
    Pipeline* p, bool& first, std::string const& key, JSON const& value, size_t depth)
91
430k
{
92
430k
    writeDictionaryKey(p, first, key, depth);
93
430k
    value.write(p, depth);
94
430k
}
95
96
void
97
JSON::writeArrayItem(Pipeline* p, bool& first, JSON const& element, size_t depth)
98
1.46M
{
99
1.46M
    writeNext(p, first, depth);
100
1.46M
    element.write(p, depth);
101
1.46M
}
102
103
void
104
JSON::JSON_dictionary::write(Pipeline* p, size_t depth) const
105
113k
{
106
113k
    bool first = true;
107
113k
    writeDictionaryOpen(p, first, depth);
108
430k
    for (auto const& iter: members) {
109
430k
        writeDictionaryItem(p, first, iter.first, iter.second, 1 + depth);
110
430k
    }
111
113k
    writeDictionaryClose(p, first, depth);
112
113k
}
113
114
void
115
JSON::JSON_array::write(Pipeline* p, size_t depth) const
116
86.5k
{
117
86.5k
    bool first = true;
118
86.5k
    writeArrayOpen(p, first, depth);
119
1.46M
    for (auto const& element: elements) {
120
1.46M
        writeArrayItem(p, first, element, 1 + depth);
121
1.46M
    }
122
86.5k
    writeArrayClose(p, first, depth);
123
86.5k
}
124
125
JSON::JSON_string::JSON_string(std::string const& utf8) :
126
3.50M
    JSON_value(vt_string),
127
3.50M
    utf8(utf8)
128
3.50M
{
129
3.50M
}
130
131
void
132
JSON::JSON_string::write(Pipeline* p, size_t) const
133
1.30M
{
134
1.30M
    *p << std::string("\"") + Writer::encode_string(utf8) + "\"";
135
1.30M
}
136
137
JSON::JSON_number::JSON_number(long long value) :
138
0
    JSON_value(vt_number),
139
0
    encoded(std::to_string(value))
140
0
{
141
0
}
142
143
JSON::JSON_number::JSON_number(double value) :
144
0
    JSON_value(vt_number),
145
0
    encoded(QUtil::double_to_string(value, 6))
146
0
{
147
0
}
148
149
JSON::JSON_number::JSON_number(std::string const& value) :
150
5.24M
    JSON_value(vt_number),
151
5.24M
    encoded(value)
152
5.24M
{
153
5.24M
}
154
155
void
156
JSON::JSON_number::write(Pipeline* p, size_t) const
157
301k
{
158
301k
    *p << encoded;
159
301k
}
160
161
JSON::JSON_bool::JSON_bool(bool val) :
162
26.2k
    JSON_value(vt_bool),
163
26.2k
    value(val)
164
26.2k
{
165
26.2k
}
166
167
void
168
JSON::JSON_bool::write(Pipeline* p, size_t) const
169
7.29k
{
170
7.29k
    *p << (value ? "true" : "false");
171
7.29k
}
172
173
void
174
JSON::JSON_null::write(Pipeline* p, size_t) const
175
118k
{
176
118k
    *p << "null";
177
118k
}
178
179
JSON::JSON_blob::JSON_blob(std::function<void(Pipeline*)> fn) :
180
0
    JSON_value(vt_blob),
181
0
    fn(fn)
182
0
{
183
0
}
184
185
void
186
JSON::JSON_blob::write(Pipeline* p, size_t) const
187
0
{
188
0
    *p << "\"";
189
0
    Pl_Concatenate cat("blob concatenate", p);
190
0
    Pl_Base64 base64("blob base64", &cat, Pl_Base64::a_encode);
191
0
    fn(&base64);
192
0
    base64.finish();
193
0
    *p << "\"";
194
0
}
195
196
void
197
JSON::write(Pipeline* p, size_t depth) const
198
1.92M
{
199
1.92M
    if (!m) {
200
0
        *p << "null";
201
1.92M
    } else {
202
1.92M
        m->value->write(p, depth);
203
1.92M
    }
204
1.92M
}
205
206
std::string
207
JSON::unparse() const
208
29.8k
{
209
29.8k
    if (!m) {
210
0
        return "null";
211
0
    }
212
29.8k
    std::string s;
213
29.8k
    Pl_String p("unparse", nullptr, s);
214
29.8k
    write(&p, 0);
215
29.8k
    return s;
216
29.8k
}
217
218
std::string
219
JSON::Writer::encode_string(std::string const& str)
220
2.25M
{
221
2.25M
    static auto constexpr hexchars = "0123456789abcdef";
222
223
2.25M
    auto begin = str.cbegin();
224
2.25M
    auto end = str.cend();
225
2.25M
    auto iter = begin;
226
473M
    while (iter != end) {
227
471M
        auto c = static_cast<unsigned char>(*iter);
228
471M
        if ((c > 34 && c != '\\') || c == ' ' || c == 33) {
229
            // Optimistically check that no char in str requires escaping. Hopefully we can just
230
            // return the input str.
231
471M
            ++iter;
232
471M
        } else {
233
            // We found a char that requires escaping. Initialize result to the chars scanned so
234
            // far, append/replace the rest of str one char at a time, and return the result.
235
90.8k
            std::string result{begin, iter};
236
237
434M
            for (; iter != end; ++iter) {
238
434M
                auto ch = static_cast<unsigned char>(*iter);
239
434M
                if ((ch > 34 && ch != '\\') || ch == ' ' || ch == 33) {
240
                    // Check for most common case first.
241
386M
                    result += *iter;
242
386M
                } else {
243
48.2M
                    switch (ch) {
244
181k
                    case '\\':
245
181k
                        result += "\\\\";
246
181k
                        break;
247
85.5k
                    case '\"':
248
85.5k
                        result += "\\\"";
249
85.5k
                        break;
250
11.7M
                    case '\b':
251
11.7M
                        result += "\\b";
252
11.7M
                        break;
253
98.7k
                    case '\f':
254
98.7k
                        result += "\\f";
255
98.7k
                        break;
256
210k
                    case '\n':
257
210k
                        result += "\\n";
258
210k
                        break;
259
11.6k
                    case '\r':
260
11.6k
                        result += "\\r";
261
11.6k
                        break;
262
5.28M
                    case '\t':
263
5.28M
                        result += "\\t";
264
5.28M
                        break;
265
30.6M
                    default:
266
30.6M
                        result += ch < 16 ? "\\u000" : "\\u001";
267
30.6M
                        result += hexchars[ch % 16];
268
48.2M
                    }
269
48.2M
                }
270
434M
            }
271
90.8k
            return result;
272
90.8k
        }
273
471M
    }
274
2.16M
    return str;
275
2.25M
}
276
277
JSON
278
JSON::makeDictionary()
279
375k
{
280
375k
    return {std::make_unique<JSON_dictionary>()};
281
375k
}
282
283
JSON
284
JSON::addDictionaryMember(std::string const& key, JSON const& val)
285
831k
{
286
831k
    if (auto* obj = m ? dynamic_cast<JSON_dictionary*>(m->value.get()) : nullptr) {
287
831k
        return obj->members[Writer::encode_string(key)] = val.m ? val : makeNull();
288
831k
    } else {
289
0
        throw std::runtime_error("JSON::addDictionaryMember called on non-dictionary");
290
0
    }
291
831k
}
292
293
JSON
294
JSON::makeArray()
295
182k
{
296
182k
    return {std::make_unique<JSON_array>()};
297
182k
}
298
299
JSON
300
JSON::addArrayElement(JSON const& val)
301
4.83M
{
302
4.83M
    if (auto* arr = m ? dynamic_cast<JSON_array*>(m->value.get()) : nullptr) {
303
4.83M
        if (val.m) {
304
4.83M
            arr->elements.push_back(val);
305
4.83M
        } else {
306
0
            arr->elements.push_back(makeNull());
307
0
        }
308
4.83M
        return arr->elements.back();
309
4.83M
    }
310
0
    throw std::runtime_error("JSON::addArrayElement called on non-array");
311
0
    return {}; // unreachable
312
4.83M
}
313
314
JSON
315
JSON::makeString(std::string const& utf8)
316
3.50M
{
317
3.50M
    return {std::make_unique<JSON_string>(utf8)};
318
3.50M
}
319
320
JSON
321
JSON::makeInt(long long int value)
322
0
{
323
0
    return {std::make_unique<JSON_number>(value)};
324
0
}
325
326
JSON
327
JSON::makeReal(double value)
328
0
{
329
0
    return {std::make_unique<JSON_number>(value)};
330
0
}
331
332
JSON
333
JSON::makeNumber(std::string const& encoded)
334
5.24M
{
335
5.24M
    return {std::make_unique<JSON_number>(encoded)};
336
5.24M
}
337
338
JSON
339
JSON::makeBool(bool value)
340
26.2k
{
341
26.2k
    return {std::make_unique<JSON_bool>(value)};
342
26.2k
}
343
344
JSON
345
JSON::makeNull()
346
129k
{
347
129k
    return {std::make_unique<JSON_null>()};
348
129k
}
349
350
JSON
351
JSON::makeBlob(std::function<void(Pipeline*)> fn)
352
0
{
353
0
    return {std::make_unique<JSON_blob>(fn)};
354
0
}
355
356
bool
357
JSON::isArray() const
358
12.6M
{
359
12.6M
    return m ? m->value->type_code == vt_array : false;
360
12.6M
}
361
362
bool
363
JSON::isDictionary() const
364
13.5M
{
365
13.5M
    return m && m->value->type_code == vt_dictionary;
366
13.5M
}
367
368
bool
369
JSON::getString(std::string& utf8) const
370
1.10M
{
371
1.10M
    if (m && m->value->type_code == vt_string) {
372
1.09M
        auto v = dynamic_cast<JSON_string const*>(m->value.get());
373
1.09M
        utf8 = v->utf8;
374
1.09M
        return true;
375
1.09M
    }
376
7.20k
    return false;
377
1.10M
}
378
379
bool
380
JSON::getNumber(std::string& value) const
381
3.35M
{
382
3.35M
    if (m && m->value->type_code == vt_number) {
383
2.28M
        auto v = dynamic_cast<JSON_number const*>(m->value.get());
384
2.28M
        value = v->encoded;
385
2.28M
        return true;
386
2.28M
    }
387
1.07M
    return false;
388
3.35M
}
389
390
bool
391
JSON::getBool(bool& value) const
392
3.36M
{
393
3.36M
    if (m && m->value->type_code == vt_bool) {
394
7.94k
        auto v = dynamic_cast<JSON_bool const*>(m->value.get());
395
7.94k
        value = v->value;
396
7.94k
        return true;
397
7.94k
    }
398
3.35M
    return false;
399
3.36M
}
400
401
bool
402
JSON::isNull() const
403
3.36M
{
404
3.36M
    return m && m->value->type_code == vt_null;
405
3.36M
}
406
407
JSON
408
JSON::getDictItem(std::string const& key) const
409
0
{
410
0
    if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) {
411
0
        if (auto it = v->members.find(key); it != v->members.end()) {
412
0
            return it->second;
413
0
        }
414
0
    }
415
0
    return makeNull();
416
0
}
417
418
bool
419
JSON::forEachDictItem(std::function<void(std::string const& key, JSON value)> fn) const
420
0
{
421
0
    if (auto v = m ? dynamic_cast<JSON_dictionary const*>(m->value.get()) : nullptr) {
422
0
        for (auto const& [key, value]: v->members) {
423
0
            fn(key, value);
424
0
        }
425
0
        return true;
426
0
    }
427
0
    return false;
428
0
}
429
430
bool
431
JSON::forEachArrayItem(std::function<void(JSON value)> fn) const
432
0
{
433
0
    if (auto v = m ? dynamic_cast<JSON_array const*>(m->value.get()) : nullptr) {
434
0
        for (auto const& i: v->elements) {
435
0
            fn(JSON(i));
436
0
        }
437
0
        return true;
438
0
    }
439
0
    return false;
440
0
}
441
442
bool
443
JSON::checkSchema(JSON schema, std::list<std::string>& errors)
444
0
{
445
0
    if (!m || !schema.m) {
446
0
        return false;
447
0
    }
448
0
    checkSchemaInternal(m->value.get(), schema.m->value.get(), 0, errors, "");
449
0
    return errors.empty();
450
0
}
451
452
bool
453
JSON::checkSchema(JSON schema, unsigned long flags, std::list<std::string>& errors)
454
0
{
455
0
    if (!m || !schema.m) {
456
0
        return false;
457
0
    }
458
0
    checkSchemaInternal(m->value.get(), schema.m->value.get(), flags, errors, "");
459
0
    return errors.empty();
460
0
}
461
462
void
463
JSON::checkSchemaInternal(
464
    JSON_value* this_v,
465
    JSON_value* sch_v,
466
    unsigned long flags,
467
    std::list<std::string>& errors,
468
    std::string prefix)
469
0
{
470
0
    auto error = [&errors, prefix](std::string const& msg) {
471
0
        if (prefix.empty()) {
472
0
            errors.emplace_back("top-level object" + msg);
473
0
        } else {
474
0
            errors.emplace_back("json key \"" + prefix + "\"" + msg);
475
0
        }
476
0
    };
477
478
0
    if (auto* sch_dict = dynamic_cast<JSON_dictionary*>(sch_v)) {
479
0
        auto* this_dict = dynamic_cast<JSON_dictionary*>(this_v);
480
0
        if (!this_dict) {
481
0
            error(" is supposed to be a dictionary");
482
0
            return;
483
0
        }
484
0
        auto const& members = sch_dict->members;
485
0
        if (members.size() == 1) {
486
0
            auto const& pattern_key = members.begin()->first;
487
0
            if (pattern_key.starts_with('<') && pattern_key.ends_with('>')) {
488
0
                auto pattern_schema = sch_dict->members[pattern_key].m->value.get();
489
0
                for (auto const& [key, val]: this_dict->members) {
490
0
                    checkSchemaInternal(
491
0
                        val.m->value.get(), pattern_schema, flags, errors, prefix + "." + key);
492
0
                }
493
0
                return;
494
0
            }
495
0
        }
496
497
0
        for (auto& [key, val]: sch_dict->members) {
498
0
            if (this_dict->members.contains(key)) {
499
0
                checkSchemaInternal(
500
0
                    this_dict->members[key].m->value.get(),
501
0
                    val.m->value.get(),
502
0
                    flags,
503
0
                    errors,
504
0
                    prefix + "." + key);
505
0
            } else {
506
0
                if (flags & f_optional) {
507
0
                    QTC::TC("libtests", "JSON optional key");
508
0
                } else {
509
0
                    error(": key \"" + key + "\" is present in schema but missing in object");
510
0
                }
511
0
            }
512
0
        }
513
0
        for (auto const& item: this_dict->members) {
514
0
            if (!sch_dict->members.contains(item.first)) {
515
0
                error(
516
0
                    ": key \"" + item.first + "\" is not present in schema but appears in object");
517
0
            }
518
0
        }
519
0
        return;
520
0
    }
521
522
0
    if (auto* sch_arr = dynamic_cast<JSON_array*>(sch_v)) {
523
0
        auto* this_arr = dynamic_cast<JSON_array*>(this_v);
524
0
        auto n_elements = sch_arr->elements.size();
525
0
        if (n_elements == 1) {
526
            // A single-element array in the schema allows a single element in the object or a
527
            // variable-length array, each of whose items must conform to the single element of the
528
            // schema array. This doesn't apply to arrays of arrays -- we fall back to the behavior
529
            // of allowing a single item only when the object is not an array.
530
0
            if (this_arr) {
531
0
                int i = 0;
532
0
                for (auto const& element: this_arr->elements) {
533
0
                    checkSchemaInternal(
534
0
                        element.m->value.get(),
535
0
                        sch_arr->elements.at(0).m->value.get(),
536
0
                        flags,
537
0
                        errors,
538
0
                        prefix + "." + std::to_string(i));
539
0
                    ++i;
540
0
                }
541
0
            } else {
542
0
                checkSchemaInternal(
543
0
                    this_v, sch_arr->elements.at(0).m->value.get(), flags, errors, prefix);
544
0
            }
545
0
        } else if (!this_arr || this_arr->elements.size() != n_elements) {
546
0
            error(" is supposed to be an array of length " + std::to_string(n_elements));
547
0
            return;
548
0
        } else {
549
            // A multi-element array in the schema must correspond to an element of the same length
550
            // in the object. Each element in the object is validated against the corresponding
551
            // element in the schema.
552
0
            size_t i = 0;
553
0
            for (auto const& element: this_arr->elements) {
554
0
                checkSchemaInternal(
555
0
                    element.m->value.get(),
556
0
                    sch_arr->elements.at(i).m->value.get(),
557
0
                    flags,
558
0
                    errors,
559
0
                    prefix + "." + std::to_string(i));
560
0
                ++i;
561
0
            }
562
0
        }
563
0
        return;
564
0
    }
565
566
0
    if (!dynamic_cast<JSON_string*>(sch_v)) {
567
0
        error(" schema value is not dictionary, array, or string");
568
0
    }
569
0
}
570
571
namespace
572
{
573
    class JSONParser
574
    {
575
      public:
576
        JSONParser(InputSource& is, JSON::Reactor* reactor) :
577
60.0k
            is(is),
578
60.0k
            reactor(reactor),
579
60.0k
            p(buf)
580
60.0k
        {
581
60.0k
        }
582
583
        JSON parse();
584
585
      private:
586
        enum parser_state_e {
587
            ps_top,
588
            ps_dict_begin,
589
            ps_dict_after_key,
590
            ps_dict_after_colon,
591
            ps_dict_after_item,
592
            ps_dict_after_comma,
593
            ps_array_begin,
594
            ps_array_after_item,
595
            ps_array_after_comma,
596
            ps_done,
597
        };
598
599
        enum lex_state_e {
600
            ls_top,
601
            ls_number,
602
            ls_number_minus,
603
            ls_number_leading_zero,
604
            ls_number_before_point,
605
            ls_number_point,
606
            ls_number_after_point,
607
            ls_number_e,
608
            ls_number_e_sign,
609
            ls_alpha,
610
            ls_string,
611
            ls_after_string,
612
            ls_backslash,
613
            ls_u4,
614
            ls_begin_array,
615
            ls_end_array,
616
            ls_begin_dict,
617
            ls_end_dict,
618
            ls_colon,
619
            ls_comma,
620
        };
621
622
        struct StackFrame
623
        {
624
            StackFrame(parser_state_e state, JSON& item) :
625
558k
                state(state),
626
558k
                item(item)
627
558k
            {
628
558k
            }
629
630
            parser_state_e state;
631
            JSON item;
632
        };
633
634
        void getToken();
635
        void handleToken();
636
        void tokenError();
637
        static void handle_u_code(
638
            unsigned long codepoint,
639
            qpdf_offset_t offset,
640
            unsigned long& high_surrogate,
641
            qpdf_offset_t& high_offset,
642
            std::string& result);
643
        inline void append();
644
        inline void append(lex_state_e);
645
        inline void ignore();
646
        inline void ignore(lex_state_e);
647
648
        InputSource& is;
649
        JSON::Reactor* reactor;
650
        lex_state_e lex_state{ls_top};
651
        char buf[16384];
652
        size_t bytes{0};
653
        char const* p;
654
        qpdf_offset_t u_count{0};
655
        unsigned long u_value{0};
656
        qpdf_offset_t offset{0};
657
        bool done{false};
658
        std::string token;
659
        qpdf_offset_t token_start{0};
660
        parser_state_e parser_state{ps_top};
661
        std::vector<StackFrame> stack;
662
        std::string dict_key;
663
        qpdf_offset_t dict_key_offset{0};
664
    };
665
} // namespace
666
667
void
668
JSONParser::handle_u_code(
669
    unsigned long codepoint,
670
    qpdf_offset_t offset,
671
    unsigned long& high_surrogate,
672
    qpdf_offset_t& high_offset,
673
    std::string& result)
674
15.3M
{
675
15.3M
    if ((codepoint & 0xFC00) == 0xD800) {
676
        // high surrogate
677
3.89k
        qpdf_offset_t new_high_offset = offset;
678
3.89k
        if (high_offset) {
679
69
            QTC::TC("libtests", "JSON 16 high high");
680
69
            throw std::runtime_error(
681
69
                "JSON: offset " + std::to_string(new_high_offset) +
682
69
                ": UTF-16 high surrogate found after previous high surrogate at offset " +
683
69
                std::to_string(high_offset));
684
69
        }
685
3.82k
        high_offset = new_high_offset;
686
3.82k
        high_surrogate = codepoint;
687
15.3M
    } else if ((codepoint & 0xFC00) == 0xDC00) {
688
        // low surrogate
689
3.76k
        if (offset != (high_offset + 6)) {
690
82
            QTC::TC("libtests", "JSON 16 low not after high");
691
82
            throw std::runtime_error(
692
82
                "JSON: offset " + std::to_string(offset) +
693
82
                ": UTF-16 low surrogate found not immediately after high surrogate");
694
82
        }
695
3.67k
        high_offset = 0;
696
3.67k
        codepoint = 0x10000U + ((high_surrogate & 0x3FFU) << 10U) + (codepoint & 0x3FF);
697
3.67k
        result += QUtil::toUTF8(codepoint);
698
15.3M
    } else {
699
15.3M
        result += QUtil::toUTF8(codepoint);
700
15.3M
    }
701
15.3M
}
702
703
void
704
JSONParser::tokenError()
705
3.43k
{
706
3.43k
    if (done) {
707
2.07k
        QTC::TC("libtests", "JSON parse ls premature end of input");
708
2.07k
        throw std::runtime_error("JSON: premature end of input");
709
2.07k
    }
710
711
1.36k
    if (lex_state == ls_u4) {
712
163
        QTC::TC("libtests", "JSON parse bad hex after u");
713
163
        throw std::runtime_error(
714
163
            "JSON: offset " + std::to_string(offset - u_count - 1) +
715
163
            ": \\u must be followed by four hex digits");
716
1.20k
    } else if (lex_state == ls_alpha) {
717
465
        QTC::TC("libtests", "JSON parse keyword bad character");
718
465
        throw std::runtime_error(
719
465
            "JSON: offset " + std::to_string(offset) + ": keyword: unexpected character " +
720
465
            std::string(p, 1));
721
737
    } else if (lex_state == ls_string) {
722
57
        QTC::TC("libtests", "JSON parse control char in string");
723
57
        throw std::runtime_error(
724
57
            "JSON: offset " + std::to_string(offset) +
725
57
            ": control character in string (missing \"?)");
726
680
    } else if (lex_state == ls_backslash) {
727
64
        QTC::TC("libtests", "JSON parse backslash bad character");
728
64
        throw std::runtime_error(
729
64
            "JSON: offset " + std::to_string(offset) +
730
64
            ": invalid character after backslash: " + std::string(p, 1));
731
64
    }
732
733
616
    if (*p == '.') {
734
32
        if (lex_state == ls_number || lex_state == ls_number_e || lex_state == ls_number_e_sign) {
735
18
            QTC::TC("libtests", "JSON parse point after e");
736
18
            throw std::runtime_error(
737
18
                "JSON: offset " + std::to_string(offset) +
738
18
                ": numeric literal: decimal point after e");
739
18
        } else {
740
14
            QTC::TC("libtests", "JSON parse duplicate point");
741
14
            throw std::runtime_error(
742
14
                "JSON: offset " + std::to_string(offset) +
743
14
                ": numeric literal: decimal point already seen");
744
14
        }
745
584
    } else if (*p == 'e' || *p == 'E') {
746
35
        QTC::TC("libtests", "JSON parse duplicate e");
747
35
        throw std::runtime_error(
748
35
            "JSON: offset " + std::to_string(offset) + ": numeric literal: e already seen");
749
549
    } else if ((*p == '+') || (*p == '-')) {
750
42
        QTC::TC("libtests", "JSON parse unexpected sign");
751
42
        throw std::runtime_error(
752
42
            "JSON: offset " + std::to_string(offset) + ": numeric literal: unexpected sign");
753
507
    } else if (util::is_space(*p) || strchr("{}[]:,", *p)) {
754
107
        QTC::TC("libtests", "JSON parse incomplete number");
755
107
        throw std::runtime_error(
756
107
            "JSON: offset " + std::to_string(offset) + ": numeric literal: incomplete number");
757
758
400
    } else {
759
400
        QTC::TC("libtests", "JSON parse numeric bad character");
760
400
        throw std::runtime_error(
761
400
            "JSON: offset " + std::to_string(offset) + ": numeric literal: unexpected character " +
762
400
            std::string(p, 1));
763
400
    }
764
0
    throw std::logic_error("JSON::tokenError : unhandled error");
765
616
}
766
767
// Append current character to token and advance to next input character.
768
inline void
769
JSONParser::append()
770
972M
{
771
972M
    token += *p;
772
972M
    ++p;
773
972M
    ++offset;
774
972M
}
775
776
// Append current character to token, advance to next input character and transition to 'next' lexer
777
// state.
778
inline void
779
JSONParser::append(lex_state_e next)
780
5.51M
{
781
5.51M
    lex_state = next;
782
5.51M
    token += *p;
783
5.51M
    ++p;
784
5.51M
    ++offset;
785
5.51M
}
786
787
// Advance to next input character without appending the current character to token.
788
inline void
789
JSONParser::ignore()
790
118M
{
791
118M
    ++p;
792
118M
    ++offset;
793
118M
}
794
795
// Advance to next input character without appending the current character to token and transition
796
// to 'next' lexer state.
797
inline void
798
JSONParser::ignore(lex_state_e next)
799
44.9M
{
800
44.9M
    lex_state = next;
801
44.9M
    ++p;
802
44.9M
    ++offset;
803
44.9M
}
804
805
void
806
JSONParser::getToken()
807
21.2M
{
808
21.2M
    token.clear();
809
810
    // Keep track of UTF-16 surrogate pairs.
811
21.2M
    unsigned long high_surrogate = 0;
812
21.2M
    qpdf_offset_t high_offset = 0;
813
814
1.14G
    while (true) {
815
1.14G
        if (p == (buf + bytes)) {
816
179k
            p = buf;
817
179k
            bytes = is.read(buf, sizeof(buf));
818
179k
            if (bytes == 0) {
819
53.7k
                done = true;
820
53.7k
                break;
821
53.7k
            }
822
179k
        }
823
824
1.14G
        if ((*p < 32 && *p >= 0)) {
825
3.14M
            if (*p == '\t' || *p == '\n' || *p == '\r') {
826
                // Legal white space not permitted in strings. This will always end the current
827
                // token (unless we are still before the start of the token).
828
3.14M
                if (lex_state == ls_top) {
829
3.05M
                    ignore();
830
3.05M
                } else {
831
84.5k
                    break;
832
84.5k
                }
833
834
3.14M
            } else {
835
313
                QTC::TC("libtests", "JSON parse null character");
836
313
                throw std::runtime_error(
837
313
                    "JSON: control or null character at offset " + std::to_string(offset));
838
313
            }
839
1.14G
        } else if (*p == ',') {
840
26.4M
            if (lex_state == ls_top) {
841
8.92M
                ignore(ls_comma);
842
8.92M
                return;
843
17.5M
            } else if (lex_state == ls_string) {
844
12.2M
                append();
845
12.2M
            } else {
846
5.29M
                break;
847
5.29M
            }
848
1.11G
        } else if (*p == ':') {
849
1.90M
            if (lex_state == ls_top) {
850
1.23M
                ignore(ls_colon);
851
1.23M
                return;
852
1.23M
            } else if (lex_state == ls_string) {
853
669k
                append();
854
669k
            } else {
855
196
                break;
856
196
            }
857
1.11G
        } else if (*p == ' ') {
858
148M
            if (lex_state == ls_top) {
859
29.5M
                ignore();
860
118M
            } else if (lex_state == ls_string) {
861
118M
                append();
862
118M
            } else {
863
803
                break;
864
803
            }
865
967M
        } else if (*p == '{') {
866
464k
            if (lex_state == ls_top) {
867
375k
                token_start = offset;
868
375k
                ignore(ls_begin_dict);
869
375k
                return;
870
375k
            } else if (lex_state == ls_string) {
871
88.2k
                append();
872
88.2k
            } else {
873
144
                break;
874
144
            }
875
966M
        } else if (*p == '}') {
876
287k
            if (lex_state == ls_top) {
877
249k
                ignore(ls_end_dict);
878
249k
                return;
879
249k
            } else if (lex_state == ls_string) {
880
27.9k
                append();
881
27.9k
            } else {
882
10.2k
                break;
883
10.2k
            }
884
966M
        } else if (*p == '[') {
885
254k
            if (lex_state == ls_top) {
886
182k
                token_start = offset;
887
182k
                ignore(ls_begin_array);
888
182k
                return;
889
182k
            } else if (lex_state == ls_string) {
890
71.7k
                append();
891
71.7k
            } else {
892
59
                break;
893
59
            }
894
966M
        } else if (*p == ']') {
895
184k
            if (lex_state == ls_top) {
896
99.5k
                ignore(ls_end_array);
897
99.5k
                return;
898
99.5k
            } else if (lex_state == ls_string) {
899
83.8k
                append();
900
83.8k
            } else {
901
1.44k
                break;
902
1.44k
            }
903
966M
        } else {
904
966M
            switch (lex_state) {
905
10.1M
            case ls_top:
906
10.1M
                token_start = offset;
907
10.1M
                if (*p == '"') {
908
4.74M
                    ignore(ls_string);
909
5.40M
                } else if ((*p >= 'a') && (*p <= 'z')) {
910
157k
                    append(ls_alpha);
911
5.24M
                } else if (*p == '-') {
912
9.85k
                    append(ls_number_minus);
913
5.23M
                } else if ((*p >= '1') && (*p <= '9')) {
914
4.73M
                    append(ls_number_before_point);
915
4.73M
                } else if (*p == '0') {
916
497k
                    append(ls_number_leading_zero);
917
497k
                } else {
918
868
                    QTC::TC("libtests", "JSON parse bad character");
919
868
                    throw std::runtime_error(
920
868
                        "JSON: offset " + std::to_string(offset) + ": unexpected character " +
921
868
                        std::string(p, 1));
922
868
                }
923
10.1M
                break;
924
925
10.1M
            case ls_number_minus:
926
9.75k
                if ((*p >= '1') && (*p <= '9')) {
927
6.42k
                    append(ls_number_before_point);
928
6.42k
                } else if (*p == '0') {
929
3.27k
                    append(ls_number_leading_zero);
930
3.27k
                } else {
931
55
                    QTC::TC("libtests", "JSON parse number minus no digits");
932
55
                    throw std::runtime_error(
933
55
                        "JSON: offset " + std::to_string(offset) +
934
55
                        ": numeric literal: no digit after minus sign");
935
55
                }
936
9.70k
                break;
937
938
12.2k
            case ls_number_leading_zero:
939
12.2k
                if (*p == '.') {
940
8.21k
                    append(ls_number_point);
941
8.21k
                } else if (*p == 'e' || *p == 'E') {
942
3.85k
                    append(ls_number_e);
943
3.85k
                } else {
944
213
                    QTC::TC("libtests", "JSON parse leading zero");
945
213
                    throw std::runtime_error(
946
213
                        "JSON: offset " + std::to_string(offset) + ": number with leading zero");
947
213
                }
948
12.0k
                break;
949
950
3.21M
            case ls_number_before_point:
951
3.21M
                if ((*p >= '0') && (*p <= '9')) {
952
3.17M
                    append();
953
3.17M
                } else if (*p == '.') {
954
15.6k
                    append(ls_number_point);
955
17.9k
                } else if (*p == 'e' || *p == 'E') {
956
17.7k
                    append(ls_number_e);
957
17.7k
                } else {
958
195
                    tokenError();
959
195
                }
960
3.21M
                break;
961
962
23.8k
            case ls_number_point:
963
23.8k
                if ((*p >= '0') && (*p <= '9')) {
964
23.7k
                    append(ls_number_after_point);
965
23.7k
                } else {
966
56
                    tokenError();
967
56
                }
968
23.8k
                break;
969
970
6.61M
            case ls_number_after_point:
971
6.61M
                if ((*p >= '0') && (*p <= '9')) {
972
6.60M
                    append();
973
6.60M
                } else if (*p == 'e' || *p == 'E') {
974
1.95k
                    append(ls_number_e);
975
1.95k
                } else {
976
66
                    tokenError();
977
66
                }
978
6.61M
                break;
979
980
23.4k
            case ls_number_e:
981
23.4k
                if ((*p >= '0') && (*p <= '9')) {
982
18.7k
                    append(ls_number);
983
18.7k
                } else if ((*p == '+') || (*p == '-')) {
984
4.55k
                    append(ls_number_e_sign);
985
4.55k
                } else {
986
70
                    tokenError();
987
70
                }
988
23.4k
                break;
989
990
4.51k
            case ls_number_e_sign:
991
4.51k
                if ((*p >= '0') && (*p <= '9')) {
992
4.45k
                    append(ls_number);
993
4.45k
                } else {
994
58
                    tokenError();
995
58
                }
996
4.51k
                break;
997
998
4.24M
            case ls_number:
999
                // We only get here after we have seen an exponent.
1000
4.24M
                if ((*p >= '0') && (*p <= '9')) {
1001
4.24M
                    append();
1002
4.24M
                } else {
1003
64
                    tokenError();
1004
64
                }
1005
4.24M
                break;
1006
1007
19.5M
            case ls_alpha:
1008
19.5M
                if ((*p >= 'a') && (*p <= 'z')) {
1009
19.5M
                    append();
1010
19.5M
                } else {
1011
465
                    tokenError();
1012
465
                }
1013
19.5M
                break;
1014
1015
836M
            case ls_string:
1016
836M
                if (*p == '"') {
1017
4.74M
                    if (high_offset) {
1018
57
                        QTC::TC("libtests", "JSON 16 dangling high");
1019
57
                        throw std::runtime_error(
1020
57
                            "JSON: offset " + std::to_string(high_offset) +
1021
57
                            ": UTF-16 high surrogate not followed by low surrogate");
1022
57
                    }
1023
4.74M
                    ignore(ls_after_string);
1024
4.74M
                    return;
1025
831M
                } else if (*p == '\\') {
1026
24.3M
                    ignore(ls_backslash);
1027
807M
                } else {
1028
807M
                    append();
1029
807M
                }
1030
831M
                break;
1031
1032
831M
            case ls_backslash:
1033
24.3M
                lex_state = ls_string;
1034
24.3M
                switch (*p) {
1035
170k
                case '\\':
1036
221k
                case '\"':
1037
222k
                case '/':
1038
                    // \/ is allowed in json input, but so is /, so we don't map / to \/ in output.
1039
222k
                    token += *p;
1040
222k
                    break;
1041
5.89M
                case 'b':
1042
5.89M
                    token += '\b';
1043
5.89M
                    break;
1044
58.8k
                case 'f':
1045
58.8k
                    token += '\f';
1046
58.8k
                    break;
1047
120k
                case 'n':
1048
120k
                    token += '\n';
1049
120k
                    break;
1050
16.6k
                case 'r':
1051
16.6k
                    token += '\r';
1052
16.6k
                    break;
1053
2.64M
                case 't':
1054
2.64M
                    token += '\t';
1055
2.64M
                    break;
1056
15.3M
                case 'u':
1057
15.3M
                    lex_state = ls_u4;
1058
15.3M
                    u_count = 0;
1059
15.3M
                    u_value = 0;
1060
15.3M
                    break;
1061
30
                default:
1062
30
                    lex_state = ls_backslash;
1063
30
                    tokenError();
1064
24.3M
                }
1065
24.3M
                ignore();
1066
24.3M
                break;
1067
1068
61.5M
            case ls_u4:
1069
61.5M
                using ui = unsigned int;
1070
61.5M
                if (ui val = ui(util::hex_decode_char(*p)); val < 16) {
1071
61.5M
                    u_value = 16 * u_value + val;
1072
61.5M
                } else {
1073
140
                    tokenError();
1074
140
                }
1075
61.5M
                if (++u_count == 4) {
1076
15.3M
                    handle_u_code(u_value, offset - 5, high_surrogate, high_offset, token);
1077
15.3M
                    lex_state = ls_string;
1078
15.3M
                }
1079
61.5M
                ignore();
1080
61.5M
                break;
1081
1082
0
            default:
1083
0
                throw std::logic_error("JSONParser::getToken : trying to handle delimiter state");
1084
966M
            }
1085
966M
        }
1086
1.14G
    }
1087
1088
    // We only get here if on end of input or if the last character was a control character or other
1089
    // delimiter.
1090
1091
5.44M
    if (!token.empty()) {
1092
5.40M
        switch (lex_state) {
1093
0
        case ls_top:
1094
            // Can't happen
1095
0
            throw std::logic_error("tok_start set in ls_top while parsing");
1096
0
            break;
1097
1098
488k
        case ls_number_leading_zero:
1099
5.20M
        case ls_number_before_point:
1100
5.22M
        case ls_number_after_point:
1101
5.22M
            lex_state = ls_number;
1102
5.22M
            break;
1103
1104
23.1k
        case ls_number:
1105
179k
        case ls_alpha:
1106
            // terminal state
1107
179k
            break;
1108
1109
2.29k
        default:
1110
2.29k
            tokenError();
1111
5.40M
        }
1112
5.40M
    }
1113
5.44M
}
1114
1115
void
1116
JSONParser::handleToken()
1117
21.2M
{
1118
21.2M
    if (lex_state == ls_top) {
1119
43.9k
        return;
1120
43.9k
    }
1121
1122
21.2M
    if (parser_state == ps_done) {
1123
128
        QTC::TC("libtests", "JSON parse junk after object");
1124
128
        throw std::runtime_error(
1125
128
            "JSON: offset " + std::to_string(offset) +
1126
128
            ": material follows end of object: " + token);
1127
128
    }
1128
1129
21.2M
    const static JSON null_item = JSON::makeNull();
1130
21.2M
    JSON item;
1131
21.2M
    auto tos = stack.empty() ? null_item : stack.back().item;
1132
21.2M
    auto ls = lex_state;
1133
21.2M
    lex_state = ls_top;
1134
1135
21.2M
    switch (ls) {
1136
375k
    case ls_begin_dict:
1137
375k
        item = JSON::makeDictionary();
1138
375k
        break;
1139
1140
182k
    case ls_begin_array:
1141
182k
        item = JSON::makeArray();
1142
182k
        break;
1143
1144
1.23M
    case ls_colon:
1145
1.23M
        if (parser_state != ps_dict_after_key) {
1146
478
            QTC::TC("libtests", "JSON parse unexpected :");
1147
478
            throw std::runtime_error(
1148
478
                "JSON: offset " + std::to_string(offset) + ": unexpected colon");
1149
478
        }
1150
1.23M
        parser_state = ps_dict_after_colon;
1151
1.23M
        return;
1152
1153
8.92M
    case ls_comma:
1154
8.92M
        if (!((parser_state == ps_dict_after_item) || (parser_state == ps_array_after_item))) {
1155
211
            QTC::TC("libtests", "JSON parse unexpected ,");
1156
211
            throw std::runtime_error(
1157
211
                "JSON: offset " + std::to_string(offset) + ": unexpected comma");
1158
211
        }
1159
8.92M
        if (parser_state == ps_dict_after_item) {
1160
916k
            parser_state = ps_dict_after_comma;
1161
8.00M
        } else if (parser_state == ps_array_after_item) {
1162
8.00M
            parser_state = ps_array_after_comma;
1163
8.00M
        } else {
1164
0
            throw std::logic_error("JSONParser::handleToken: unexpected parser state for comma");
1165
0
        }
1166
8.92M
        return;
1167
1168
8.92M
    case ls_end_array:
1169
99.5k
        if (!(parser_state == ps_array_begin || parser_state == ps_array_after_item)) {
1170
54
            QTC::TC("libtests", "JSON parse unexpected ]");
1171
54
            throw std::runtime_error(
1172
54
                "JSON: offset " + std::to_string(offset) + ": unexpected array end delimiter");
1173
54
        }
1174
99.5k
        parser_state = stack.back().state;
1175
99.5k
        tos.setEnd(offset);
1176
99.5k
        if (reactor) {
1177
5.82k
            reactor->containerEnd(tos);
1178
5.82k
        }
1179
99.5k
        if (parser_state != ps_done) {
1180
99.4k
            stack.pop_back();
1181
99.4k
        }
1182
99.5k
        return;
1183
1184
249k
    case ls_end_dict:
1185
249k
        if (!((parser_state == ps_dict_begin) || (parser_state == ps_dict_after_item))) {
1186
42
            QTC::TC("libtests", "JSON parse unexpected }");
1187
42
            throw std::runtime_error(
1188
42
                "JSON: offset " + std::to_string(offset) + ": unexpected dictionary end delimiter");
1189
42
        }
1190
249k
        parser_state = stack.back().state;
1191
249k
        tos.setEnd(offset);
1192
249k
        if (reactor) {
1193
67.8k
            reactor->containerEnd(tos);
1194
67.8k
        }
1195
249k
        if (parser_state != ps_done) {
1196
219k
            stack.pop_back();
1197
219k
        }
1198
249k
        return;
1199
1200
5.24M
    case ls_number:
1201
5.24M
        item = JSON::makeNumber(token);
1202
5.24M
        break;
1203
1204
156k
    case ls_alpha:
1205
156k
        if (token == "true") {
1206
19.1k
            item = JSON::makeBool(true);
1207
137k
        } else if (token == "false") {
1208
7.08k
            item = JSON::makeBool(false);
1209
130k
        } else if (token == "null") {
1210
129k
            item = JSON::makeNull();
1211
129k
        } else {
1212
840
            QTC::TC("libtests", "JSON parse invalid keyword");
1213
840
            throw std::runtime_error(
1214
840
                "JSON: offset " + std::to_string(offset) + ": invalid keyword " + token);
1215
840
        }
1216
155k
        break;
1217
1218
4.74M
    case ls_after_string:
1219
4.74M
        if (parser_state == ps_dict_begin || parser_state == ps_dict_after_comma) {
1220
1.23M
            dict_key = token;
1221
1.23M
            dict_key_offset = token_start;
1222
1.23M
            parser_state = ps_dict_after_key;
1223
1.23M
            return;
1224
3.50M
        } else {
1225
3.50M
            item = JSON::makeString(token);
1226
3.50M
        }
1227
3.50M
        break;
1228
1229
3.50M
    default:
1230
506
        throw std::runtime_error(
1231
506
            "JSON: offset " + std::to_string(offset) + ": premature end of input");
1232
0
        break;
1233
21.2M
    }
1234
1235
9.46M
    item.setStart(token_start);
1236
9.46M
    item.setEnd(offset);
1237
1238
9.46M
    switch (parser_state) {
1239
66
    case ps_dict_begin:
1240
150
    case ps_dict_after_comma:
1241
150
        QTC::TC("libtests", "JSON parse string as dict key");
1242
150
        throw std::runtime_error(
1243
150
            "JSON: offset " + std::to_string(offset) + ": expect string as dictionary key");
1244
0
        break;
1245
1246
1.23M
    case ps_dict_after_colon:
1247
1.23M
        if (!reactor || !reactor->dictionaryItem(dict_key, item)) {
1248
831k
            tos.addDictionaryMember(dict_key, item);
1249
831k
        }
1250
1.23M
        parser_state = ps_dict_after_item;
1251
1.23M
        break;
1252
1253
171k
    case ps_array_begin:
1254
8.17M
    case ps_array_after_comma:
1255
8.17M
        if (!reactor || !reactor->arrayItem(item)) {
1256
4.83M
            tos.addArrayElement(item);
1257
4.83M
        }
1258
8.17M
        parser_state = ps_array_after_item;
1259
8.17M
        break;
1260
1261
57.8k
    case ps_top:
1262
57.8k
        if (!(item.isDictionary() || item.isArray())) {
1263
406
            stack.emplace_back(ps_done, item);
1264
406
            parser_state = ps_done;
1265
406
            return;
1266
406
        }
1267
57.4k
        parser_state = ps_done;
1268
57.4k
        break;
1269
1270
129
    case ps_dict_after_key:
1271
129
        QTC::TC("libtests", "JSON parse expected colon");
1272
129
        throw std::runtime_error("JSON: offset " + std::to_string(offset) + ": expected ':'");
1273
0
        break;
1274
1275
245
    case ps_dict_after_item:
1276
245
        QTC::TC("libtests", "JSON parse expected , or }");
1277
245
        throw std::runtime_error(
1278
245
            "JSON: offset " + std::to_string(offset) + ": expected ',' or '}'");
1279
0
        break;
1280
1281
194
    case ps_array_after_item:
1282
194
        QTC::TC("libtests", "JSON parse expected, or ]");
1283
194
        throw std::runtime_error(
1284
194
            "JSON: offset " + std::to_string(offset) + ": expected ',' or ']'");
1285
0
        break;
1286
1287
0
    case ps_done:
1288
0
        throw std::logic_error("JSONParser::handleToken: unexpected parser state");
1289
9.46M
    }
1290
1291
9.46M
    if (item.isDictionary() || item.isArray()) {
1292
558k
        stack.emplace_back(parser_state, item);
1293
        // Calling container start method is postponed until after adding the containers to their
1294
        // parent containers, if any. This makes it much easier to keep track of the current nesting
1295
        // level.
1296
558k
        if (item.isDictionary()) {
1297
375k
            if (reactor) {
1298
130k
                reactor->dictionaryStart();
1299
130k
            }
1300
375k
            parser_state = ps_dict_begin;
1301
375k
        } else if (item.isArray()) {
1302
182k
            if (reactor) {
1303
43.1k
                reactor->arrayStart();
1304
43.1k
            }
1305
182k
            parser_state = ps_array_begin;
1306
182k
        }
1307
1308
558k
        if (stack.size() > 500) {
1309
12
            throw std::runtime_error(
1310
12
                "JSON: offset " + std::to_string(offset) + ": maximum object depth exceeded");
1311
12
        }
1312
558k
    }
1313
9.46M
}
1314
1315
JSON
1316
JSONParser::parse()
1317
60.0k
{
1318
21.3M
    while (!done) {
1319
21.2M
        getToken();
1320
21.2M
        handleToken();
1321
21.2M
    }
1322
60.0k
    if (parser_state != ps_done) {
1323
19.8k
        QTC::TC("libtests", "JSON parse premature EOF");
1324
19.8k
        throw std::runtime_error("JSON: premature end of input");
1325
19.8k
    }
1326
40.1k
    auto const& tos = stack.back().item;
1327
40.1k
    if (reactor && !(tos.isArray() || tos.isDictionary())) {
1328
124
        reactor->topLevelScalar();
1329
124
    }
1330
40.1k
    return tos;
1331
60.0k
}
1332
1333
JSON
1334
JSON::parse(InputSource& is, Reactor* reactor)
1335
15.0k
{
1336
15.0k
    JSONParser jp(is, reactor);
1337
15.0k
    return jp.parse();
1338
15.0k
}
1339
1340
JSON
1341
JSON::parse(std::string const& s)
1342
44.9k
{
1343
44.9k
    is::OffsetBuffer bis("json input", s);
1344
44.9k
    JSONParser jp(bis, nullptr);
1345
44.9k
    return jp.parse();
1346
44.9k
}
1347
1348
void
1349
JSON::setStart(qpdf_offset_t start)
1350
9.46M
{
1351
9.46M
    if (m) {
1352
9.46M
        m->start = start;
1353
9.46M
    }
1354
9.46M
}
1355
1356
void
1357
JSON::setEnd(qpdf_offset_t end)
1358
9.81M
{
1359
9.81M
    if (m) {
1360
9.81M
        m->end = end;
1361
9.81M
    }
1362
9.81M
}
1363
1364
qpdf_offset_t
1365
JSON::getStart() const
1366
2.59M
{
1367
2.59M
    return m ? m->start : 0;
1368
2.59M
}
1369
1370
qpdf_offset_t
1371
JSON::getEnd() const
1372
13.2k
{
1373
13.2k
    return m ? m->end : 0;
1374
13.2k
}