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 error->offset = json - head;
325 error->error = lastError;
326 }
327 return QCborValue();
328}
329
330// We need to retain the _last_ value for any duplicate keys and we need to deref containers.
331// Therefore the manual implementation of std::unique().
332template<typename Iterator, typename Compare, typename Assign>
333static Iterator customAssigningUniqueLast(Iterator first, Iterator last,
334 Compare compare, Assign assign)
335{
336 first = std::adjacent_find(first, last, compare);
337 if (first == last)
338 return last;
339
340 // After adjacent_find, we know that *first and *(first+1) compare equal,
341 // and that first+1 != last.
342 Iterator result = first++;
343 Q_ASSERT(compare(*result, *first));
344 assign(*result, *first);
345 Q_ASSERT(first != last);
346
347 while (++first != last) {
348 if (!compare(*result, *first))
349 ++result;
350
351 // Due to adjacent_find above, we know that we've at least eliminated one element.
352 // Therefore we have to move each further element across the gap.
353 Q_ASSERT(result != first);
354
355 // We have to overwrite each element we want to eliminate, to deref() the container.
356 // Therefore we don't try to optimize the number of assignments here.
357 assign(*result, *first);
358 }
359
360 return ++result;
361}
362
364{
365 using Forward = QJsonPrivate::KeyIterator;
366 using Value = Forward::value_type;
367
368 auto compare = [container](const Value &a, const Value &b)
369 {
370 const auto &aKey = a.key();
371 const auto &bKey = b.key();
372
373 Q_ASSERT(aKey.flags & QtCbor::Element::HasByteData);
374 Q_ASSERT(bKey.flags & QtCbor::Element::HasByteData);
375
376 const QtCbor::ByteData *aData = container->byteData(aKey);
377 const QtCbor::ByteData *bData = container->byteData(bKey);
378
379 if (!aData)
380 return bData ? -1 : 0;
381 if (!bData)
382 return 1;
383
384 // US-ASCII (StringIsAscii flag) is just a special case of UTF-8
385 // string, so we can safely ignore the flag.
386
387 if (aKey.flags & QtCbor::Element::StringIsUtf16) {
388 if (bKey.flags & QtCbor::Element::StringIsUtf16)
389 return QtPrivate::compareStrings(aData->asStringView(), bData->asStringView());
390
391 return -QCborContainerPrivate::compareUtf8(bData, aData->asStringView());
392 } else {
393 if (bKey.flags & QtCbor::Element::StringIsUtf16)
394 return QCborContainerPrivate::compareUtf8(aData, bData->asStringView());
395
396 return QtPrivate::compareStrings(aData->asUtf8StringView(), bData->asUtf8StringView());
397 }
398 };
399
400 // The elements' containers are owned by the outer container, not by the elements themselves.
401 auto move = [](Forward::reference target, Forward::reference source)
402 {
403 QtCbor::Element &targetValue = target.value();
404
405 // If the target has a container, deref it before overwriting, so that we don't leak.
406 if (targetValue.flags & QtCbor::Element::IsContainer)
407 targetValue.container->deref();
408
409 // Do not move, so that we can clear the value afterwards.
410 target = source;
411
412 // Clear the source value, so that we don't store the same container twice.
413 source.value() = QtCbor::Element();
414 };
415
416 std::stable_sort(
417 Forward(container->elements.begin()), Forward(container->elements.end()),
418 [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; });
419
420 Forward result = customAssigningUniqueLast(
421 Forward(container->elements.begin()), Forward(container->elements.end()),
422 [&compare](const Value &a, const Value &b) { return compare(a, b) == 0; }, move);
423
424 container->elements.erase(result.elementsIterator(), container->elements.end());
425}
426
427bool Parser::parseValueIntoContainer()
428{
429 QCborValue value = parseValue();
430 switch (value.type()) {
431 case QCborValue::Undefined:
432 return false; // error while parsing
433 case QCborValue::String:
434 break; // strings were already added
435 default:
436 container->append(std::move(value));
437 }
438
439 return true;
440}
441
442/*
443 object = begin-object [ member *( value-separator member ) ]
444 end-object
445*/
446
447bool Parser::parseObject()
448{
449 if (++nestingLevel > nestingLimit) {
450 lastError = QJsonParseError::DeepNesting;
451 return false;
452 }
453
454 char token = nextToken();
455 while (token == Quote) {
456 if (!container)
457 container = new QCborContainerPrivate;
458 if (!parseMember())
459 return false;
460 token = nextToken();
461 if (token != ValueSeparator)
462 break;
463 token = nextToken();
464 if (token == EndObject) {
465 lastError = QJsonParseError::MissingObject;
466 return false;
467 }
468 }
469
470 if (token != EndObject) {
471 lastError = QJsonParseError::UnterminatedObject;
472 return false;
473 }
474
475 --nestingLevel;
476
477 if (container)
478 sortContainer(container.data());
479 return true;
480}
481
482/*
483 member = string name-separator value
484*/
485bool Parser::parseMember()
486{
487 if (!parseString())
488 return false;
489 char token = nextToken();
490 if (token != NameSeparator) {
491 lastError = QJsonParseError::MissingNameSeparator;
492 return false;
493 }
494 if (!eatSpace()) {
495 lastError = QJsonParseError::UnterminatedObject;
496 return false;
497 }
498
499 return parseValueIntoContainer();
500}
501
502/*
503 array = begin-array [ value *( value-separator value ) ] end-array
504*/
505bool Parser::parseArray()
506{
507 if (++nestingLevel > nestingLimit) {
508 lastError = QJsonParseError::DeepNesting;
509 return false;
510 }
511
512 if (!eatSpace()) {
513 lastError = QJsonParseError::UnterminatedArray;
514 return false;
515 }
516 if (*json == EndArray) {
517 nextToken();
518 } else {
519 while (1) {
520 if (!eatSpace()) {
521 lastError = QJsonParseError::UnterminatedArray;
522 return false;
523 }
524 if (!container)
525 container = new QCborContainerPrivate;
526
527 if (!parseValueIntoContainer())
528 return false;
529
530 char token = nextToken();
531 if (token == EndArray)
532 break;
533 else if (token != ValueSeparator) {
534 if (!eatSpace())
535 lastError = QJsonParseError::UnterminatedArray;
536 else
537 lastError = QJsonParseError::MissingValueSeparator;
538 return false;
539 }
540 }
541 }
542
543 --nestingLevel;
544
545 return true;
546}
547
548/*
549value = false / null / true / object / array / number / string
550
551*/
552
553QCborValue Parser::parseValue()
554{
555 switch (*json++) {
556 case 'n':
557 if (end - json < 3) {
558 lastError = QJsonParseError::IllegalValue;
559 return QCborValue();
560 }
561 if (*json++ == 'u' &&
562 *json++ == 'l' &&
563 *json++ == 'l') {
564 return QCborValue(QCborValue::Null);
565 }
566 lastError = QJsonParseError::IllegalValue;
567 return QCborValue();
568 case 't':
569 if (end - json < 3) {
570 lastError = QJsonParseError::IllegalValue;
571 return QCborValue();
572 }
573 if (*json++ == 'r' &&
574 *json++ == 'u' &&
575 *json++ == 'e') {
576 return QCborValue(true);
577 }
578 lastError = QJsonParseError::IllegalValue;
579 return QCborValue();
580 case 'f':
581 if (end - json < 4) {
582 lastError = QJsonParseError::IllegalValue;
583 return QCborValue();
584 }
585 if (*json++ == 'a' &&
586 *json++ == 'l' &&
587 *json++ == 's' &&
588 *json++ == 'e') {
589 return QCborValue(false);
590 }
591 lastError = QJsonParseError::IllegalValue;
592 return QCborValue();
593 case Quote: {
594 if (parseString())
595 // strings are already added to the container
596 // callers must check for this type
597 return QCborValue(QCborValue::String);
598
599 return QCborValue();
600 }
601 case BeginArray: {
602 StashedContainer stashedContainer(&container, QCborValue::Array);
603 if (parseArray())
604 return stashedContainer.intoValue(&container);
605
606 return QCborValue();
607 }
608 case BeginObject: {
609 StashedContainer stashedContainer(&container, QCborValue::Map);
610 if (parseObject())
611 return stashedContainer.intoValue(&container);
612
613 return QCborValue();
614 }
615 case ValueSeparator:
616 // Essentially missing value, but after a colon, not after a comma
617 // like the other MissingObject errors.
618 lastError = QJsonParseError::IllegalValue;
619 return QCborValue();
620 case EndObject:
621 case EndArray:
622 lastError = QJsonParseError::MissingObject;
623 return QCborValue();
624 default:
625 --json;
626 return parseNumber();
627 }
628}
629
630
631
632
633
634/*
635 number = [ minus ] int [ frac ] [ exp ]
636 decimal-point = %x2E ; .
637 digit1-9 = %x31-39 ; 1-9
638 e = %x65 / %x45 ; e E
639 exp = e [ minus / plus ] 1*DIGIT
640 frac = decimal-point 1*DIGIT
641 int = zero / ( digit1-9 *DIGIT )
642 minus = %x2D ; -
643 plus = %x2B ; +
644 zero = %x30 ; 0
645
646*/
647
648QCborValue Parser::parseNumber()
649{
650 const char *start = json;
651 bool isInt = true;
652
653 // minus
654 if (json < end && *json == '-')
655 ++json;
656
657 // int = zero / ( digit1-9 *DIGIT )
658 if (json < end && *json == '0') {
659 ++json;
660 } else {
661 while (json < end && isAsciiDigit(*json))
662 ++json;
663 }
664
665 // frac = decimal-point 1*DIGIT
666 if (json < end && *json == '.') {
667 ++json;
668 while (json < end && isAsciiDigit(*json)) {
669 isInt = isInt && *json == '0';
670 ++json;
671 }
672 }
673
674 // exp = e [ minus / plus ] 1*DIGIT
675 if (json < end && (*json == 'e' || *json == 'E')) {
676 isInt = false;
677 ++json;
678 if (json < end && (*json == '-' || *json == '+'))
679 ++json;
680 while (json < end && isAsciiDigit(*json))
681 ++json;
682 }
683
684 const QByteArray number = QByteArray::fromRawData(start, json - start);
685
686 if (isInt) {
687 bool ok;
688 qlonglong n = number.toLongLong(&ok);
689 if (ok) {
690 return QCborValue(n);
691 }
692 }
693
694 bool ok;
695 double d = number.toDouble(&ok);
696
697 if (!ok) {
698 lastError = QJsonParseError::IllegalNumber;
699 return QCborValue();
700 }
701
702 qint64 n;
703 if (convertDoubleTo(d, &n))
704 return QCborValue(n);
705 return QCborValue(d);
706}
707
708/*
709
710 string = quotation-mark *char quotation-mark
711
712 char = unescaped /
713 escape (
714 %x22 / ; " quotation mark U+0022
715 %x5C / ; \ reverse solidus U+005C
716 %x2F / ; / solidus U+002F
717 %x62 / ; b backspace U+0008
718 %x66 / ; f form feed U+000C
719 %x6E / ; n line feed U+000A
720 %x72 / ; r carriage return U+000D
721 %x74 / ; t tab U+0009
722 %x75 4HEXDIG ) ; uXXXX U+XXXX
723
724 escape = %x5C ; \
725
726 quotation-mark = %x22 ; "
727
728 unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
729 */
730static inline bool addHexDigit(char digit, char32_t *result)
731{
732 *result <<= 4;
733 const int h = fromHex(digit);
734 if (h != -1) {
735 *result |= h;
736 return true;
737 }
738
739 return false;
740}
741
742static inline bool scanEscapeSequence(const char *&json, const char *end, char32_t *ch)
743{
744 ++json;
745 if (json >= end)
746 return false;
747
748 uchar escaped = *json++;
749 switch (escaped) {
750 case '"':
751 *ch = '"'; break;
752 case '\\':
753 *ch = '\\'; break;
754 case '/':
755 *ch = '/'; break;
756 case 'b':
757 *ch = 0x8; break;
758 case 'f':
759 *ch = 0xc; break;
760 case 'n':
761 *ch = 0xa; break;
762 case 'r':
763 *ch = 0xd; break;
764 case 't':
765 *ch = 0x9; break;
766 case 'u': {
767 *ch = 0;
768 if (json > end - 4)
769 return false;
770 for (int i = 0; i < 4; ++i) {
771 if (!addHexDigit(*json, ch))
772 return false;
773 ++json;
774 }
775 return true;
776 }
777 default:
778 // this is not as strict as one could be, but allows for more Json files
779 // to be parsed correctly.
780 *ch = escaped;
781 return true;
782 }
783 return true;
784}
785
786static inline bool scanUtf8Char(const char *&json, const char *end, char32_t *result)
787{
788 auto usrc = reinterpret_cast<const qchar8_t*>(json);
789 const auto uend = reinterpret_cast<const qchar8_t*>(end);
790 constexpr char32_t Invalid = ~U'\0';
791 const char32_t ch = QUtf8Functions::nextUcs4FromUtf8(usrc, uend, Invalid);
792 if (ch == Invalid)
793 return false;
794 *result = ch;
795 json = reinterpret_cast<const char *>(usrc);
796 return true;
797}
798
799bool Parser::parseString()
800{
801 const char *start = json;
802
803 // try to parse a utf-8 string without escape sequences, and note whether it's 7bit ASCII.
804
805 bool isUtf8 = true;
806 bool isAscii = true;
807 while (json < end) {
808 char32_t ch = 0;
809 if (*json == '"')
810 break;
811 if (*json == '\\') {
812 isAscii = false;
813 // If we find escape sequences, we store UTF-16 as there are some
814 // escape sequences which are hard to represent in UTF-8.
815 // (plain "\\ud800" for example)
816 isUtf8 = false;
817 break;
818 }
819 if (!scanUtf8Char(json, end, &ch)) {
820 lastError = QJsonParseError::IllegalUTF8String;
821 return false;
822 }
823 if (ch > 0x7f)
824 isAscii = false;
825 }
826 ++json;
827 if (json > end) {
828 lastError = QJsonParseError::UnterminatedString;
829 return false;
830 }
831
832 // no escape sequences, we are done
833 if (isUtf8) {
834 if (isAscii)
835 container->appendAsciiString(start, json - start - 1);
836 else
837 container->appendUtf8String(start, json - start - 1);
838 return true;
839 }
840
841 json = start;
842
843 QString ucs4;
844 while (json < end) {
845 char32_t ch = 0;
846 if (*json == '"')
847 break;
848 else if (*json == '\\') {
849 if (!scanEscapeSequence(json, end, &ch)) {
850 lastError = QJsonParseError::IllegalEscapeSequence;
851 return false;
852 }
853 } else {
854 if (!scanUtf8Char(json, end, &ch)) {
855 lastError = QJsonParseError::IllegalUTF8String;
856 return false;
857 }
858 }
859 ucs4.append(QChar::fromUcs4(ch));
860 }
861 ++json;
862
863 if (json > end) {
864 lastError = QJsonParseError::UnterminatedString;
865 return false;
866 }
867
868 container->appendByteData(reinterpret_cast<const char *>(ucs4.constData()), ucs4.size() * 2,
869 QCborValue::String, QtCbor::Element::StringIsUtf16);
870 return true;
871}
872
873QT_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