Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qjsonparser.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2021 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4// Qt-Security score:critical reason:data-parser
5
6#ifndef QT_BOOTSTRAPPED
7#include <qcoreapplication.h>
8#endif
9#include <qdebug.h>
10#include "qjsonparser_p.h"
11#include "qjson_p.h"
12#include "private/qstringconverter_p.h"
13#include "private/qcborvalue_p.h"
14#include "private/qnumeric_p.h"
15#include <private/qtools_p.h>
16
17static const int nestingLimit = 1024;
18
19QT_BEGIN_NAMESPACE
20
21using namespace QtMiscUtils;
22
23// error strings for the JSON parser
24#define JSONERR_OK QT_TRANSLATE_NOOP("QJsonParseError", "no error occurred")
25#define JSONERR_UNTERM_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "unterminated object")
26#define JSONERR_MISS_NSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing name separator")
27#define JSONERR_UNTERM_AR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated array")
28#define JSONERR_MISS_VSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing value separator")
29#define JSONERR_ILLEGAL_VAL QT_TRANSLATE_NOOP("QJsonParseError", "illegal value")
30#define JSONERR_END_OF_NUM QT_TRANSLATE_NOOP("QJsonParseError", "invalid termination by number")
31#define JSONERR_ILLEGAL_NUM QT_TRANSLATE_NOOP("QJsonParseError", "illegal number")
32#define JSONERR_STR_ESC_SEQ QT_TRANSLATE_NOOP("QJsonParseError", "invalid escape sequence")
33#define JSONERR_STR_UTF8 QT_TRANSLATE_NOOP("QJsonParseError", "invalid UTF8 string")
34#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string")
35#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma")
36#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document")
37#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document")
38#define JSONERR_GARBAGEEND QT_TRANSLATE_NOOP("QJsonParseError", "garbage at the end of the document")
39
40/*!
41 \class QJsonParseError
42 \inmodule QtCore
43 \ingroup json
44 \ingroup shared
45 \ingroup qtserialization
46 \reentrant
47 \since 5.0
48
49 \brief The QJsonParseError class is used to report errors during JSON parsing.
50
51 \sa {JSON Support in Qt}, {Saving and Loading a Game}
52*/
53
54/*!
55 \enum QJsonParseError::ParseError
56
57 This enum describes the type of error that occurred during the parsing of a JSON document.
58
59 \value NoError No error occurred
60 \value UnterminatedObject An object is not correctly terminated with a closing curly bracket
61 \value MissingNameSeparator A comma separating different items is missing
62 \value UnterminatedArray The array is not correctly terminated with a closing square bracket
63 \value MissingValueSeparator A colon separating keys from values inside objects is missing
64 \value IllegalValue The value is illegal
65 \value TerminationByNumber The input stream ended while parsing a number (as of 6.9, this is no longer returned)
66 \value IllegalNumber The number is not well formed
67 \value IllegalEscapeSequence An illegal escape sequence occurred in the input
68 \value IllegalUTF8String An illegal UTF8 sequence occurred in the input
69 \value UnterminatedString A string wasn't terminated with a quote
70 \value MissingObject An object was expected but couldn't be found
71 \value DeepNesting The JSON document is too deeply nested for the parser to parse it
72 \value DocumentTooLarge The JSON document is too large for the parser to parse it
73 \value GarbageAtEnd The parsed document contains additional garbage characters at the end
74
75*/
76
77/*!
78 \variable QJsonParseError::error
79
80 Contains the type of the parse error. Is equal to QJsonParseError::NoError if the document
81 was parsed correctly.
82
83 \sa ParseError, errorString()
84*/
85
86
87/*!
88 \variable QJsonParseError::offset
89
90 Contains the byte offset in the UTF-8 byte array where the parse error occurred.
91
92 \sa error, errorString(), QJsonDocument::fromJson()
93*/
94
95/*!
96 Returns the human-readable message appropriate to the reported JSON parsing error.
97
98 \sa error
99 */
100QString QJsonParseError::errorString() const
101{
102 const char *sz = "";
103 switch (error) {
104 case NoError:
105 sz = JSONERR_OK;
106 break;
107 case UnterminatedObject:
109 break;
110 case MissingNameSeparator:
112 break;
113 case UnterminatedArray:
115 break;
116 case MissingValueSeparator:
118 break;
119 case IllegalValue:
121 break;
122 case TerminationByNumber:
124 break;
125 case IllegalNumber:
127 break;
128 case IllegalEscapeSequence:
130 break;
131 case IllegalUTF8String:
132 sz = JSONERR_STR_UTF8;
133 break;
134 case UnterminatedString:
136 break;
137 case MissingObject:
138 sz = JSONERR_MISS_OBJ;
139 break;
140 case DeepNesting:
142 break;
143 case DocumentTooLarge:
145 break;
146 case GarbageAtEnd:
148 break;
149 }
150#ifndef QT_BOOTSTRAPPED
151 return QCoreApplication::translate("QJsonParseError", sz);
152#else
153 return QLatin1StringView(sz);
154#endif
155}
156
157using namespace QJsonPrivate;
158
180
181Parser::Parser(QUtf8StringView json)
182 : head(json.data()), json(head), end(json.end())
183 , nestingLevel(0)
185{
186}
187
188
189
190/*
191
192begin-array = ws %x5B ws ; [ left square bracket
193
194begin-object = ws %x7B ws ; { left curly bracket
195
196end-array = ws %x5D ws ; ] right square bracket
197
198end-object = ws %x7D ws ; } right curly bracket
199
200name-separator = ws %x3A ws ; : colon
201
202value-separator = ws %x2C ws ; , comma
203
204Insignificant whitespace is allowed before or after any of the six
205structural characters.
206
207ws = *(
208 %x20 / ; Space
209 %x09 / ; Horizontal tab
210 %x0A / ; Line feed or New line
211 %x0D ; Carriage return
212 )
213
214*/
215
216enum {
217 Space = 0x20,
218 Tab = 0x09,
219 LineFeed = 0x0a,
220 Return = 0x0d,
223 EndArray = 0x5d,
224 EndObject = 0x7d,
227 Quote = 0x22
228};
229
230void Parser::eatBOM()
231{
232 // eat UTF-8 byte order mark
233 uchar utf8bom[3] = { 0xef, 0xbb, 0xbf };
234 if (end - json > 3 &&
235 (uchar)json[0] == utf8bom[0] &&
236 (uchar)json[1] == utf8bom[1] &&
237 (uchar)json[2] == utf8bom[2])
238 json += 3;
239}
240
241bool Parser::eatSpace()
242{
243 while (json < end) {
244 if (*json > Space)
245 break;
246 if (*json != Space &&
247 *json != Tab &&
248 *json != LineFeed &&
249 *json != Return)
250 break;
251 ++json;
252 }
253 return (json < end);
254}
255
256char Parser::nextToken()
257{
258 if (!eatSpace())
259 return 0;
260 char token = *json++;
261 switch (token) {
262 case BeginArray:
263 case BeginObject:
264 case NameSeparator:
265 case ValueSeparator:
266 case EndArray:
267 case EndObject:
268 case Quote:
269 break;
270 default:
271 token = 0;
272 break;
273 }
274 return token;
275}
276
277/*
278 JSON-text = object / array
279*/
280QCborValue Parser::parse(QJsonParseError *error)
281{
282 eatBOM();
283
284 char token;
285 QCborValue value;
286
287 if (!eatSpace()) {
288 lastError = QJsonParseError::IllegalValue;
289 goto error;
290 }
291
292 token = *json;
293 if (token == Quote) {
294 container = new QCborContainerPrivate;
295 json++;
296 if (!parseString())
297 goto error;
298 value = QCborContainerPrivate::makeValue(QCborValue::String, 0, container.take(),
299 QCborContainerPrivate::MoveContainer);
300 } else {
301 value = parseValue();
302 if (value.isUndefined())
303 goto error;
304 }
305
306 eatSpace();
307 if (json < end) {
308 lastError = QJsonParseError::GarbageAtEnd;
309 goto error;
310 }
311
312 {
313 if (error) {
314 error->offset = 0;
315 error->error = QJsonParseError::NoError;
316 }
317
318 return value;
319 }
320
321error:
322 container.reset();
323 if (error) {
324 using OffType = decltype(error->offset);
325 error->offset = OffType(json - head);
326 Q_ASSERT(error->offset == json - head);
327 error->error = lastError;
328 }
329 return QCborValue();
330}
331
332// We need to retain the _last_ value for any duplicate keys and we need to deref containers.
333// Therefore the manual implementation of std::unique().
334template<typename Iterator, typename Compare, typename Assign>
335static Iterator customAssigningUniqueLast(Iterator first, Iterator last,
336 Compare compare, Assign assign)
337{
338 first = std::adjacent_find(first, last, compare);
339 if (first == last)
340 return last;
341
342 // After adjacent_find, we know that *first and *(first+1) compare equal,
343 // and that first+1 != last.
344 Iterator result = first++;
345 Q_ASSERT(compare(*result, *first));
346 assign(*result, *first);
347 Q_ASSERT(first != last);
348
349 while (++first != last) {
350 if (!compare(*result, *first))
351 ++result;
352
353 // Due to adjacent_find above, we know that we've at least eliminated one element.
354 // Therefore we have to move each further element across the gap.
355 Q_ASSERT(result != first);
356
357 // We have to overwrite each element we want to eliminate, to deref() the container.
358 // Therefore we don't try to optimize the number of assignments here.
359 assign(*result, *first);
360 }
361
362 return ++result;
363}
364
366{
367 using Forward = QJsonPrivate::KeyIterator;
368 using Value = Forward::value_type;
369
370 auto compare = [container](const Value &a, const Value &b)
371 {
372 const auto &aKey = a.key();
373 const auto &bKey = b.key();
374
375 Q_ASSERT(aKey.flags & QtCbor::Element::HasByteData);
376 Q_ASSERT(bKey.flags & QtCbor::Element::HasByteData);
377
378 const QtCbor::ByteData *aData = container->byteData(aKey);
379 const QtCbor::ByteData *bData = container->byteData(bKey);
380
381 if (!aData)
382 return bData ? -1 : 0;
383 if (!bData)
384 return 1;
385
386 // US-ASCII (StringIsAscii flag) is just a special case of UTF-8
387 // string, so we can safely ignore the flag.
388
389 if (aKey.flags & QtCbor::Element::StringIsUtf16) {
390 if (bKey.flags & QtCbor::Element::StringIsUtf16)
391 return QtPrivate::compareStrings(aData->asStringView(), bData->asStringView());
392
393 return -QCborContainerPrivate::compareUtf8(bData, aData->asStringView());
394 } else {
395 if (bKey.flags & QtCbor::Element::StringIsUtf16)
396 return QCborContainerPrivate::compareUtf8(aData, bData->asStringView());
397
398 return QtPrivate::compareStrings(aData->asUtf8StringView(), bData->asUtf8StringView());
399 }
400 };
401
402 // The elements' containers are owned by the outer container, not by the elements themselves.
403 auto move = [](Forward::reference target, Forward::reference source)
404 {
405 QtCbor::Element &targetValue = target.value();
406
407 // If the target has a container, deref it before overwriting, so that we don't leak.
408 if (targetValue.flags & QtCbor::Element::IsContainer)
409 targetValue.container->deref();
410
411 // Do not move, so that we can clear the value afterwards.
412 target = source;
413
414 // Clear the source value, so that we don't store the same container twice.
415 source.value() = QtCbor::Element();
416 };
417
418 std::stable_sort(
419 Forward(container->elements.begin()), Forward(container->elements.end()),
420 [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; });
421
422 Forward result = customAssigningUniqueLast(
423 Forward(container->elements.begin()), Forward(container->elements.end()),
424 [&compare](const Value &a, const Value &b) { return compare(a, b) == 0; }, move);
425
426 container->elements.erase(result.elementsIterator(), container->elements.end());
427}
428
429bool Parser::parseValueIntoContainer()
430{
431 QCborValue value = parseValue();
432 switch (value.type()) {
433 case QCborValue::Undefined:
434 return false; // error while parsing
435 case QCborValue::String:
436 break; // strings were already added
437 default:
438 container->append(std::move(value));
439 }
440
441 return true;
442}
443
444/*
445 object = begin-object [ member *( value-separator member ) ]
446 end-object
447*/
448
449bool Parser::parseObject()
450{
451 if (++nestingLevel > nestingLimit) {
452 lastError = QJsonParseError::DeepNesting;
453 return false;
454 }
455
456 char token = nextToken();
457 while (token == Quote) {
458 if (!container)
459 container = new QCborContainerPrivate;
460 if (!parseMember())
461 return false;
462 token = nextToken();
463 if (token != ValueSeparator)
464 break;
465 token = nextToken();
466 if (token == EndObject) {
467 lastError = QJsonParseError::MissingObject;
468 return false;
469 }
470 }
471
472 if (token != EndObject) {
473 lastError = QJsonParseError::UnterminatedObject;
474 return false;
475 }
476
477 --nestingLevel;
478
479 if (container)
480 sortContainer(container.data());
481 return true;
482}
483
484/*
485 member = string name-separator value
486*/
487bool Parser::parseMember()
488{
489 if (!parseString())
490 return false;
491 char token = nextToken();
492 if (token != NameSeparator) {
493 lastError = QJsonParseError::MissingNameSeparator;
494 return false;
495 }
496 if (!eatSpace()) {
497 lastError = QJsonParseError::UnterminatedObject;
498 return false;
499 }
500
501 return parseValueIntoContainer();
502}
503
504/*
505 array = begin-array [ value *( value-separator value ) ] end-array
506*/
507bool Parser::parseArray()
508{
509 if (++nestingLevel > nestingLimit) {
510 lastError = QJsonParseError::DeepNesting;
511 return false;
512 }
513
514 if (!eatSpace()) {
515 lastError = QJsonParseError::UnterminatedArray;
516 return false;
517 }
518 if (*json == EndArray) {
519 nextToken();
520 } else {
521 while (1) {
522 if (!eatSpace()) {
523 lastError = QJsonParseError::UnterminatedArray;
524 return false;
525 }
526 if (!container)
527 container = new QCborContainerPrivate;
528
529 if (!parseValueIntoContainer())
530 return false;
531
532 char token = nextToken();
533 if (token == EndArray)
534 break;
535 else if (token != ValueSeparator) {
536 if (!eatSpace())
537 lastError = QJsonParseError::UnterminatedArray;
538 else
539 lastError = QJsonParseError::MissingValueSeparator;
540 return false;
541 }
542 }
543 }
544
545 --nestingLevel;
546
547 return true;
548}
549
550/*
551value = false / null / true / object / array / number / string
552
553*/
554
555QCborValue Parser::parseValue()
556{
557 switch (*json++) {
558 case 'n':
559 if (end - json < 3) {
560 lastError = QJsonParseError::IllegalValue;
561 return QCborValue();
562 }
563 if (*json++ == 'u' &&
564 *json++ == 'l' &&
565 *json++ == 'l') {
566 return QCborValue(QCborValue::Null);
567 }
568 lastError = QJsonParseError::IllegalValue;
569 return QCborValue();
570 case 't':
571 if (end - json < 3) {
572 lastError = QJsonParseError::IllegalValue;
573 return QCborValue();
574 }
575 if (*json++ == 'r' &&
576 *json++ == 'u' &&
577 *json++ == 'e') {
578 return QCborValue(true);
579 }
580 lastError = QJsonParseError::IllegalValue;
581 return QCborValue();
582 case 'f':
583 if (end - json < 4) {
584 lastError = QJsonParseError::IllegalValue;
585 return QCborValue();
586 }
587 if (*json++ == 'a' &&
588 *json++ == 'l' &&
589 *json++ == 's' &&
590 *json++ == 'e') {
591 return QCborValue(false);
592 }
593 lastError = QJsonParseError::IllegalValue;
594 return QCborValue();
595 case Quote: {
596 if (parseString())
597 // strings are already added to the container
598 // callers must check for this type
599 return QCborValue(QCborValue::String);
600
601 return QCborValue();
602 }
603 case BeginArray: {
604 StashedContainer stashedContainer(&container, QCborValue::Array);
605 if (parseArray())
606 return stashedContainer.intoValue(&container);
607
608 return QCborValue();
609 }
610 case BeginObject: {
611 StashedContainer stashedContainer(&container, QCborValue::Map);
612 if (parseObject())
613 return stashedContainer.intoValue(&container);
614
615 return QCborValue();
616 }
617 case ValueSeparator:
618 // Essentially missing value, but after a colon, not after a comma
619 // like the other MissingObject errors.
620 lastError = QJsonParseError::IllegalValue;
621 return QCborValue();
622 case EndObject:
623 case EndArray:
624 lastError = QJsonParseError::MissingObject;
625 return QCborValue();
626 default:
627 --json;
628 return parseNumber();
629 }
630}
631
632
633
634
635
636/*
637 number = [ minus ] int [ frac ] [ exp ]
638 decimal-point = %x2E ; .
639 digit1-9 = %x31-39 ; 1-9
640 e = %x65 / %x45 ; e E
641 exp = e [ minus / plus ] 1*DIGIT
642 frac = decimal-point 1*DIGIT
643 int = zero / ( digit1-9 *DIGIT )
644 minus = %x2D ; -
645 plus = %x2B ; +
646 zero = %x30 ; 0
647
648*/
649
650QCborValue Parser::parseNumber()
651{
652 const char *start = json;
653 bool isInt = true;
654
655 // minus
656 if (json < end && *json == '-')
657 ++json;
658
659 // int = zero / ( digit1-9 *DIGIT )
660 if (json < end && *json == '0') {
661 ++json;
662 } else {
663 while (json < end && isAsciiDigit(*json))
664 ++json;
665 }
666
667 // frac = decimal-point 1*DIGIT
668 if (json < end && *json == '.') {
669 ++json;
670 while (json < end && isAsciiDigit(*json)) {
671 isInt = isInt && *json == '0';
672 ++json;
673 }
674 }
675
676 // exp = e [ minus / plus ] 1*DIGIT
677 if (json < end && (*json == 'e' || *json == 'E')) {
678 isInt = false;
679 ++json;
680 if (json < end && (*json == '-' || *json == '+'))
681 ++json;
682 while (json < end && isAsciiDigit(*json))
683 ++json;
684 }
685
686 const QByteArray number = QByteArray::fromRawData(start, json - start);
687
688 if (isInt) {
689 bool ok;
690 qlonglong n = number.toLongLong(&ok);
691 if (ok) {
692 return QCborValue(n);
693 }
694 }
695
696 bool ok;
697 double d = number.toDouble(&ok);
698
699 if (!ok) {
700 lastError = QJsonParseError::IllegalNumber;
701 return QCborValue();
702 }
703
704 qint64 n;
705 if (convertDoubleTo(d, &n))
706 return QCborValue(n);
707 return QCborValue(d);
708}
709
710/*
711
712 string = quotation-mark *char quotation-mark
713
714 char = unescaped /
715 escape (
716 %x22 / ; " quotation mark U+0022
717 %x5C / ; \ reverse solidus U+005C
718 %x2F / ; / solidus U+002F
719 %x62 / ; b backspace U+0008
720 %x66 / ; f form feed U+000C
721 %x6E / ; n line feed U+000A
722 %x72 / ; r carriage return U+000D
723 %x74 / ; t tab U+0009
724 %x75 4HEXDIG ) ; uXXXX U+XXXX
725
726 escape = %x5C ; \
727
728 quotation-mark = %x22 ; "
729
730 unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
731 */
732static inline bool addHexDigit(char digit, char32_t *result)
733{
734 *result <<= 4;
735 const int h = fromHex(digit);
736 if (h != -1) {
737 *result |= h;
738 return true;
739 }
740
741 return false;
742}
743
744static inline bool scanEscapeSequence(const char *&json, const char *end, char32_t *ch)
745{
746 ++json;
747 if (json >= end)
748 return false;
749
750 uchar escaped = *json++;
751 switch (escaped) {
752 case '"':
753 *ch = '"'; break;
754 case '\\':
755 *ch = '\\'; break;
756 case '/':
757 *ch = '/'; break;
758 case 'b':
759 *ch = 0x8; break;
760 case 'f':
761 *ch = 0xc; break;
762 case 'n':
763 *ch = 0xa; break;
764 case 'r':
765 *ch = 0xd; break;
766 case 't':
767 *ch = 0x9; break;
768 case 'u': {
769 *ch = 0;
770 if (json > end - 4)
771 return false;
772 for (int i = 0; i < 4; ++i) {
773 if (!addHexDigit(*json, ch))
774 return false;
775 ++json;
776 }
777 return true;
778 }
779 default:
780 // this is not as strict as one could be, but allows for more Json files
781 // to be parsed correctly.
782 *ch = escaped;
783 return true;
784 }
785 return true;
786}
787
788static inline bool scanUtf8Char(const char *&json, const char *end, char32_t *result)
789{
790 auto usrc = reinterpret_cast<const qchar8_t*>(json);
791 const auto uend = reinterpret_cast<const qchar8_t*>(end);
792 constexpr char32_t Invalid = ~U'\0';
793 const char32_t ch = QUtf8Functions::nextUcs4FromUtf8(usrc, uend, Invalid);
794 if (ch == Invalid)
795 return false;
796 *result = ch;
797 json = reinterpret_cast<const char *>(usrc);
798 return true;
799}
800
801bool Parser::parseString()
802{
803 const char *start = json;
804
805 // try to parse a utf-8 string without escape sequences, and note whether it's 7bit ASCII.
806
807 bool isUtf8 = true;
808 bool isAscii = true;
809 while (json < end) {
810 char32_t ch = 0;
811 if (*json == '"')
812 break;
813 if (*json == '\\') {
814 isAscii = false;
815 // If we find escape sequences, we store UTF-16 as there are some
816 // escape sequences which are hard to represent in UTF-8.
817 // (plain "\\ud800" for example)
818 isUtf8 = false;
819 break;
820 }
821 if (!scanUtf8Char(json, end, &ch)) {
822 lastError = QJsonParseError::IllegalUTF8String;
823 return false;
824 }
825 if (ch > 0x7f)
826 isAscii = false;
827 }
828 ++json;
829 if (json > end) {
830 lastError = QJsonParseError::UnterminatedString;
831 return false;
832 }
833
834 // no escape sequences, we are done
835 if (isUtf8) {
836 if (isAscii)
837 container->appendAsciiString(start, json - start - 1);
838 else
839 container->appendUtf8String(start, json - start - 1);
840 return true;
841 }
842
843 json = start;
844
845 QString ucs4;
846 while (json < end) {
847 char32_t ch = 0;
848 if (*json == '"')
849 break;
850 else if (*json == '\\') {
851 if (!scanEscapeSequence(json, end, &ch)) {
852 lastError = QJsonParseError::IllegalEscapeSequence;
853 return false;
854 }
855 } else {
856 if (!scanUtf8Char(json, end, &ch)) {
857 lastError = QJsonParseError::IllegalUTF8String;
858 return false;
859 }
860 }
861 ucs4.append(QChar::fromUcs4(ch));
862 }
863 ++json;
864
865 if (json > end) {
866 lastError = QJsonParseError::UnterminatedString;
867 return false;
868 }
869
870 container->appendByteData(reinterpret_cast<const char *>(ucs4.constData()), ucs4.size() * 2,
871 QCborValue::String, QtCbor::Element::StringIsUtf16);
872 return true;
873}
874
875QT_END_NAMESPACE
\inmodule QtCore\reentrant
Definition qcborvalue.h:48
QCborValue parse(QJsonParseError *error)
Parser(QUtf8StringView json)
#define JSONERR_UNTERM_AR
#define JSONERR_MISS_VSEP
#define JSONERR_UNTERM_OBJ
@ BeginObject
@ LineFeed
@ EndObject
@ BeginArray
@ ValueSeparator
@ Quote
@ Tab
@ Space
@ NameSeparator
@ EndArray
@ Return
static bool scanEscapeSequence(const char *&json, const char *end, char32_t *ch)
static const int nestingLimit
#define JSONERR_ILLEGAL_NUM
#define JSONERR_DOC_LARGE
#define JSONERR_DEEP_NEST
#define JSONERR_UTERM_STR
#define JSONERR_END_OF_NUM
static bool scanUtf8Char(const char *&json, const char *end, char32_t *result)
#define JSONERR_STR_UTF8
#define JSONERR_STR_ESC_SEQ
#define JSONERR_OK
#define JSONERR_ILLEGAL_VAL
static bool addHexDigit(char digit, char32_t *result)
static void sortContainer(QCborContainerPrivate *container)
#define JSONERR_MISS_NSEP
#define JSONERR_GARBAGEEND
static Iterator customAssigningUniqueLast(Iterator first, Iterator last, Compare compare, Assign assign)
#define JSONERR_MISS_OBJ