Coverage Report

Created: 2025-08-26 07:13

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