summaryrefslogtreecommitdiffstats
path: root/src/corelib/serialization/qjsonparser.cpp
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2017-12-12 18:32:19 -0800
committerThiago Macieira <thiago.macieira@intel.com>2018-01-26 20:59:50 +0000
commita6b697ca13945a174cff9f3e9b1af1cf61c0bea5 (patch)
tree78d5fad97ff8e89f079df29cc5a75b4e7197bfc7 /src/corelib/serialization/qjsonparser.cpp
parent657894624521b580f59ff5f58b9c0e9be159dc1c (diff)
Create corelib/serialization and move existing file formats into it
This is in preparation to adding CBOR support. We don't need yet another dir for CBOR and placing it in src/corelib/json is just wrong. Change-Id: I9741f017961b410c910dfffd14ffb9d870340fa6 Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@qt.io> Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/corelib/serialization/qjsonparser.cpp')
-rw-r--r--src/corelib/serialization/qjsonparser.cpp1027
1 files changed, 1027 insertions, 0 deletions
diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp
new file mode 100644
index 0000000000..39738b90a8
--- /dev/null
+++ b/src/corelib/serialization/qjsonparser.cpp
@@ -0,0 +1,1027 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QT_BOOTSTRAPPED
+#include <qcoreapplication.h>
+#endif
+#include <qdebug.h>
+#include "qjsonparser_p.h"
+#include "qjson_p.h"
+#include "private/qutfcodec_p.h"
+
+//#define PARSER_DEBUG
+#ifdef PARSER_DEBUG
+static int indent = 0;
+#define BEGIN qDebug() << QByteArray(4*indent++, ' ').constData() << "pos=" << current
+#define END --indent
+#define DEBUG qDebug() << QByteArray(4*indent, ' ').constData()
+#else
+#define BEGIN if (1) ; else qDebug()
+#define END do {} while (0)
+#define DEBUG if (1) ; else qDebug()
+#endif
+
+static const int nestingLimit = 1024;
+
+QT_BEGIN_NAMESPACE
+
+// error strings for the JSON parser
+#define JSONERR_OK QT_TRANSLATE_NOOP("QJsonParseError", "no error occurred")
+#define JSONERR_UNTERM_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "unterminated object")
+#define JSONERR_MISS_NSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing name separator")
+#define JSONERR_UNTERM_AR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated array")
+#define JSONERR_MISS_VSEP QT_TRANSLATE_NOOP("QJsonParseError", "missing value separator")
+#define JSONERR_ILLEGAL_VAL QT_TRANSLATE_NOOP("QJsonParseError", "illegal value")
+#define JSONERR_END_OF_NUM QT_TRANSLATE_NOOP("QJsonParseError", "invalid termination by number")
+#define JSONERR_ILLEGAL_NUM QT_TRANSLATE_NOOP("QJsonParseError", "illegal number")
+#define JSONERR_STR_ESC_SEQ QT_TRANSLATE_NOOP("QJsonParseError", "invalid escape sequence")
+#define JSONERR_STR_UTF8 QT_TRANSLATE_NOOP("QJsonParseError", "invalid UTF8 string")
+#define JSONERR_UTERM_STR QT_TRANSLATE_NOOP("QJsonParseError", "unterminated string")
+#define JSONERR_MISS_OBJ QT_TRANSLATE_NOOP("QJsonParseError", "object is missing after a comma")
+#define JSONERR_DEEP_NEST QT_TRANSLATE_NOOP("QJsonParseError", "too deeply nested document")
+#define JSONERR_DOC_LARGE QT_TRANSLATE_NOOP("QJsonParseError", "too large document")
+#define JSONERR_GARBAGEEND QT_TRANSLATE_NOOP("QJsonParseError", "garbage at the end of the document")
+
+/*!
+ \class QJsonParseError
+ \inmodule QtCore
+ \ingroup json
+ \ingroup shared
+ \reentrant
+ \since 5.0
+
+ \brief The QJsonParseError class is used to report errors during JSON parsing.
+
+ \sa {JSON Support in Qt}, {JSON Save Game Example}
+*/
+
+/*!
+ \enum QJsonParseError::ParseError
+
+ This enum describes the type of error that occurred during the parsing of a JSON document.
+
+ \value NoError No error occurred
+ \value UnterminatedObject An object is not correctly terminated with a closing curly bracket
+ \value MissingNameSeparator A comma separating different items is missing
+ \value UnterminatedArray The array is not correctly terminated with a closing square bracket
+ \value MissingValueSeparator A colon separating keys from values inside objects is missing
+ \value IllegalValue The value is illegal
+ \value TerminationByNumber The input stream ended while parsing a number
+ \value IllegalNumber The number is not well formed
+ \value IllegalEscapeSequence An illegal escape sequence occurred in the input
+ \value IllegalUTF8String An illegal UTF8 sequence occurred in the input
+ \value UnterminatedString A string wasn't terminated with a quote
+ \value MissingObject An object was expected but couldn't be found
+ \value DeepNesting The JSON document is too deeply nested for the parser to parse it
+ \value DocumentTooLarge The JSON document is too large for the parser to parse it
+ \value GarbageAtEnd The parsed document contains additional garbage characters at the end
+
+*/
+
+/*!
+ \variable QJsonParseError::error
+
+ Contains the type of the parse error. Is equal to QJsonParseError::NoError if the document
+ was parsed correctly.
+
+ \sa ParseError, errorString()
+*/
+
+
+/*!
+ \variable QJsonParseError::offset
+
+ Contains the offset in the input string where the parse error occurred.
+
+ \sa error, errorString()
+*/
+
+/*!
+ Returns the human-readable message appropriate to the reported JSON parsing error.
+
+ \sa error
+ */
+QString QJsonParseError::errorString() const
+{
+ const char *sz = "";
+ switch (error) {
+ case NoError:
+ sz = JSONERR_OK;
+ break;
+ case UnterminatedObject:
+ sz = JSONERR_UNTERM_OBJ;
+ break;
+ case MissingNameSeparator:
+ sz = JSONERR_MISS_NSEP;
+ break;
+ case UnterminatedArray:
+ sz = JSONERR_UNTERM_AR;
+ break;
+ case MissingValueSeparator:
+ sz = JSONERR_MISS_VSEP;
+ break;
+ case IllegalValue:
+ sz = JSONERR_ILLEGAL_VAL;
+ break;
+ case TerminationByNumber:
+ sz = JSONERR_END_OF_NUM;
+ break;
+ case IllegalNumber:
+ sz = JSONERR_ILLEGAL_NUM;
+ break;
+ case IllegalEscapeSequence:
+ sz = JSONERR_STR_ESC_SEQ;
+ break;
+ case IllegalUTF8String:
+ sz = JSONERR_STR_UTF8;
+ break;
+ case UnterminatedString:
+ sz = JSONERR_UTERM_STR;
+ break;
+ case MissingObject:
+ sz = JSONERR_MISS_OBJ;
+ break;
+ case DeepNesting:
+ sz = JSONERR_DEEP_NEST;
+ break;
+ case DocumentTooLarge:
+ sz = JSONERR_DOC_LARGE;
+ break;
+ case GarbageAtEnd:
+ sz = JSONERR_GARBAGEEND;
+ break;
+ }
+#ifndef QT_BOOTSTRAPPED
+ return QCoreApplication::translate("QJsonParseError", sz);
+#else
+ return QLatin1String(sz);
+#endif
+}
+
+using namespace QJsonPrivate;
+
+Parser::Parser(const char *json, int length)
+ : head(json), json(json), data(0), dataLength(0), current(0), nestingLevel(0), lastError(QJsonParseError::NoError)
+{
+ end = json + length;
+}
+
+
+
+/*
+
+begin-array = ws %x5B ws ; [ left square bracket
+
+begin-object = ws %x7B ws ; { left curly bracket
+
+end-array = ws %x5D ws ; ] right square bracket
+
+end-object = ws %x7D ws ; } right curly bracket
+
+name-separator = ws %x3A ws ; : colon
+
+value-separator = ws %x2C ws ; , comma
+
+Insignificant whitespace is allowed before or after any of the six
+structural characters.
+
+ws = *(
+ %x20 / ; Space
+ %x09 / ; Horizontal tab
+ %x0A / ; Line feed or New line
+ %x0D ; Carriage return
+ )
+
+*/
+
+enum {
+ Space = 0x20,
+ Tab = 0x09,
+ LineFeed = 0x0a,
+ Return = 0x0d,
+ BeginArray = 0x5b,
+ BeginObject = 0x7b,
+ EndArray = 0x5d,
+ EndObject = 0x7d,
+ NameSeparator = 0x3a,
+ ValueSeparator = 0x2c,
+ Quote = 0x22
+};
+
+void Parser::eatBOM()
+{
+ // eat UTF-8 byte order mark
+ uchar utf8bom[3] = { 0xef, 0xbb, 0xbf };
+ if (end - json > 3 &&
+ (uchar)json[0] == utf8bom[0] &&
+ (uchar)json[1] == utf8bom[1] &&
+ (uchar)json[2] == utf8bom[2])
+ json += 3;
+}
+
+bool Parser::eatSpace()
+{
+ while (json < end) {
+ if (*json > Space)
+ break;
+ if (*json != Space &&
+ *json != Tab &&
+ *json != LineFeed &&
+ *json != Return)
+ break;
+ ++json;
+ }
+ return (json < end);
+}
+
+char Parser::nextToken()
+{
+ if (!eatSpace())
+ return 0;
+ char token = *json++;
+ switch (token) {
+ case BeginArray:
+ case BeginObject:
+ case NameSeparator:
+ case ValueSeparator:
+ case EndArray:
+ case EndObject:
+ case Quote:
+ break;
+ default:
+ token = 0;
+ break;
+ }
+ return token;
+}
+
+/*
+ JSON-text = object / array
+*/
+QJsonDocument Parser::parse(QJsonParseError *error)
+{
+#ifdef PARSER_DEBUG
+ indent = 0;
+ qDebug(">>>>> parser begin");
+#endif
+ // allocate some space
+ dataLength = qMax(end - json, (ptrdiff_t) 256);
+ data = (char *)malloc(dataLength);
+
+ // fill in Header data
+ QJsonPrivate::Header *h = (QJsonPrivate::Header *)data;
+ h->tag = QJsonDocument::BinaryFormatTag;
+ h->version = 1u;
+
+ current = sizeof(QJsonPrivate::Header);
+
+ eatBOM();
+ char token = nextToken();
+
+ DEBUG << hex << (uint)token;
+ if (token == BeginArray) {
+ if (!parseArray())
+ goto error;
+ } else if (token == BeginObject) {
+ if (!parseObject())
+ goto error;
+ } else {
+ lastError = QJsonParseError::IllegalValue;
+ goto error;
+ }
+
+ eatSpace();
+ if (json < end) {
+ lastError = QJsonParseError::GarbageAtEnd;
+ goto error;
+ }
+
+ END;
+ {
+ if (error) {
+ error->offset = 0;
+ error->error = QJsonParseError::NoError;
+ }
+ QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current);
+ return QJsonDocument(d);
+ }
+
+error:
+#ifdef PARSER_DEBUG
+ qDebug(">>>>> parser error");
+#endif
+ if (error) {
+ error->offset = json - head;
+ error->error = lastError;
+ }
+ free(data);
+ return QJsonDocument();
+}
+
+
+void Parser::ParsedObject::insert(uint offset) {
+ const QJsonPrivate::Entry *newEntry = reinterpret_cast<const QJsonPrivate::Entry *>(parser->data + objectPosition + offset);
+ int min = 0;
+ int n = offsets.size();
+ while (n > 0) {
+ int half = n >> 1;
+ int middle = min + half;
+ if (*entryAt(middle) >= *newEntry) {
+ n = half;
+ } else {
+ min = middle + 1;
+ n -= half + 1;
+ }
+ }
+ if (min < offsets.size() && *entryAt(min) == *newEntry) {
+ offsets[min] = offset;
+ } else {
+ offsets.insert(min, offset);
+ }
+}
+
+/*
+ object = begin-object [ member *( value-separator member ) ]
+ end-object
+*/
+
+bool Parser::parseObject()
+{
+ if (++nestingLevel > nestingLimit) {
+ lastError = QJsonParseError::DeepNesting;
+ return false;
+ }
+
+ int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object));
+ if (objectOffset < 0)
+ return false;
+ BEGIN << "parseObject pos=" << objectOffset << current << json;
+
+ ParsedObject parsedObject(this, objectOffset);
+
+ char token = nextToken();
+ while (token == Quote) {
+ int off = current - objectOffset;
+ if (!parseMember(objectOffset))
+ return false;
+ parsedObject.insert(off);
+ token = nextToken();
+ if (token != ValueSeparator)
+ break;
+ token = nextToken();
+ if (token == EndObject) {
+ lastError = QJsonParseError::MissingObject;
+ return false;
+ }
+ }
+
+ DEBUG << "end token=" << token;
+ if (token != EndObject) {
+ lastError = QJsonParseError::UnterminatedObject;
+ return false;
+ }
+
+ DEBUG << "numEntries" << parsedObject.offsets.size();
+ int table = objectOffset;
+ // finalize the object
+ if (parsedObject.offsets.size()) {
+ int tableSize = parsedObject.offsets.size()*sizeof(uint);
+ table = reserveSpace(tableSize);
+ if (table < 0)
+ return false;
+
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ memcpy(data + table, parsedObject.offsets.constData(), tableSize);
+#else
+ offset *o = (offset *)(data + table);
+ for (int i = 0; i < parsedObject.offsets.size(); ++i)
+ o[i] = parsedObject.offsets[i];
+
+#endif
+ }
+
+ QJsonPrivate::Object *o = (QJsonPrivate::Object *)(data + objectOffset);
+ o->tableOffset = table - objectOffset;
+ o->size = current - objectOffset;
+ o->is_object = true;
+ o->length = parsedObject.offsets.size();
+
+ DEBUG << "current=" << current;
+ END;
+
+ --nestingLevel;
+ return true;
+}
+
+/*
+ member = string name-separator value
+*/
+bool Parser::parseMember(int baseOffset)
+{
+ int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry));
+ if (entryOffset < 0)
+ return false;
+ BEGIN << "parseMember pos=" << entryOffset;
+
+ bool latin1;
+ if (!parseString(&latin1))
+ return false;
+ char token = nextToken();
+ if (token != NameSeparator) {
+ lastError = QJsonParseError::MissingNameSeparator;
+ return false;
+ }
+ if (!eatSpace()) {
+ lastError = QJsonParseError::UnterminatedObject;
+ return false;
+ }
+ QJsonPrivate::Value val;
+ if (!parseValue(&val, baseOffset))
+ return false;
+
+ // finalize the entry
+ QJsonPrivate::Entry *e = (QJsonPrivate::Entry *)(data + entryOffset);
+ e->value = val;
+ e->value.latinKey = latin1;
+
+ END;
+ return true;
+}
+
+namespace {
+ struct ValueArray {
+ static const int prealloc = 128;
+ ValueArray() : data(stackValues), alloc(prealloc), size(0) {}
+ ~ValueArray() { if (data != stackValues) free(data); }
+
+ inline bool grow() {
+ alloc *= 2;
+ if (data == stackValues) {
+ QJsonPrivate::Value *newValues = static_cast<QJsonPrivate::Value *>(malloc(alloc*sizeof(QJsonPrivate::Value)));
+ if (!newValues)
+ return false;
+ memcpy(newValues, data, size*sizeof(QJsonPrivate::Value));
+ data = newValues;
+ } else {
+ void *newValues = realloc(data, alloc * sizeof(QJsonPrivate::Value));
+ if (!newValues)
+ return false;
+ data = static_cast<QJsonPrivate::Value *>(newValues);
+ }
+ return true;
+ }
+ bool append(const QJsonPrivate::Value &v) {
+ if (alloc == size && !grow())
+ return false;
+ data[size] = v;
+ ++size;
+ return true;
+ }
+
+ QJsonPrivate::Value stackValues[prealloc];
+ QJsonPrivate::Value *data;
+ int alloc;
+ int size;
+ };
+}
+
+/*
+ array = begin-array [ value *( value-separator value ) ] end-array
+*/
+bool Parser::parseArray()
+{
+ BEGIN << "parseArray";
+
+ if (++nestingLevel > nestingLimit) {
+ lastError = QJsonParseError::DeepNesting;
+ return false;
+ }
+
+ int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array));
+ if (arrayOffset < 0)
+ return false;
+
+ ValueArray values;
+
+ if (!eatSpace()) {
+ lastError = QJsonParseError::UnterminatedArray;
+ return false;
+ }
+ if (*json == EndArray) {
+ nextToken();
+ } else {
+ while (1) {
+ if (!eatSpace()) {
+ lastError = QJsonParseError::UnterminatedArray;
+ return false;
+ }
+ QJsonPrivate::Value val;
+ if (!parseValue(&val, arrayOffset))
+ return false;
+ if (!values.append(val)) {
+ lastError = QJsonParseError::DocumentTooLarge;
+ return false;
+ }
+ char token = nextToken();
+ if (token == EndArray)
+ break;
+ else if (token != ValueSeparator) {
+ if (!eatSpace())
+ lastError = QJsonParseError::UnterminatedArray;
+ else
+ lastError = QJsonParseError::MissingValueSeparator;
+ return false;
+ }
+ }
+ }
+
+ DEBUG << "size =" << values.size;
+ int table = arrayOffset;
+ // finalize the object
+ if (values.size) {
+ int tableSize = values.size*sizeof(QJsonPrivate::Value);
+ table = reserveSpace(tableSize);
+ if (table < 0)
+ return false;
+ memcpy(data + table, values.data, tableSize);
+ }
+
+ QJsonPrivate::Array *a = (QJsonPrivate::Array *)(data + arrayOffset);
+ a->tableOffset = table - arrayOffset;
+ a->size = current - arrayOffset;
+ a->is_object = false;
+ a->length = values.size;
+
+ DEBUG << "current=" << current;
+ END;
+
+ --nestingLevel;
+ return true;
+}
+
+/*
+value = false / null / true / object / array / number / string
+
+*/
+
+bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset)
+{
+ BEGIN << "parse Value" << json;
+ val->_dummy = 0;
+
+ switch (*json++) {
+ case 'n':
+ if (end - json < 4) {
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'u' &&
+ *json++ == 'l' &&
+ *json++ == 'l') {
+ val->type = QJsonValue::Null;
+ DEBUG << "value: null";
+ END;
+ return true;
+ }
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case 't':
+ if (end - json < 4) {
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'r' &&
+ *json++ == 'u' &&
+ *json++ == 'e') {
+ val->type = QJsonValue::Bool;
+ val->value = true;
+ DEBUG << "value: true";
+ END;
+ return true;
+ }
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case 'f':
+ if (end - json < 5) {
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ }
+ if (*json++ == 'a' &&
+ *json++ == 'l' &&
+ *json++ == 's' &&
+ *json++ == 'e') {
+ val->type = QJsonValue::Bool;
+ val->value = false;
+ DEBUG << "value: false";
+ END;
+ return true;
+ }
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case Quote: {
+ val->type = QJsonValue::String;
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = QJsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = current - baseOffset;
+ bool latin1;
+ if (!parseString(&latin1))
+ return false;
+ val->latinOrIntValue = latin1;
+ DEBUG << "value: string";
+ END;
+ return true;
+ }
+ case BeginArray:
+ val->type = QJsonValue::Array;
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = QJsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = current - baseOffset;
+ if (!parseArray())
+ return false;
+ DEBUG << "value: array";
+ END;
+ return true;
+ case BeginObject:
+ val->type = QJsonValue::Object;
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = QJsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = current - baseOffset;
+ if (!parseObject())
+ return false;
+ DEBUG << "value: object";
+ END;
+ return true;
+ case ValueSeparator:
+ // Essentially missing value, but after a colon, not after a comma
+ // like the other MissingObject errors.
+ lastError = QJsonParseError::IllegalValue;
+ return false;
+ case EndObject:
+ case EndArray:
+ lastError = QJsonParseError::MissingObject;
+ return false;
+ default:
+ --json;
+ if (!parseNumber(val, baseOffset))
+ return false;
+ DEBUG << "value: number";
+ END;
+ }
+
+ return true;
+}
+
+
+
+
+
+/*
+ number = [ minus ] int [ frac ] [ exp ]
+ decimal-point = %x2E ; .
+ digit1-9 = %x31-39 ; 1-9
+ e = %x65 / %x45 ; e E
+ exp = e [ minus / plus ] 1*DIGIT
+ frac = decimal-point 1*DIGIT
+ int = zero / ( digit1-9 *DIGIT )
+ minus = %x2D ; -
+ plus = %x2B ; +
+ zero = %x30 ; 0
+
+*/
+
+bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset)
+{
+ BEGIN << "parseNumber" << json;
+ val->type = QJsonValue::Double;
+
+ const char *start = json;
+ bool isInt = true;
+
+ // minus
+ if (json < end && *json == '-')
+ ++json;
+
+ // int = zero / ( digit1-9 *DIGIT )
+ if (json < end && *json == '0') {
+ ++json;
+ } else {
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ // frac = decimal-point 1*DIGIT
+ if (json < end && *json == '.') {
+ isInt = false;
+ ++json;
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ // exp = e [ minus / plus ] 1*DIGIT
+ if (json < end && (*json == 'e' || *json == 'E')) {
+ isInt = false;
+ ++json;
+ if (json < end && (*json == '-' || *json == '+'))
+ ++json;
+ while (json < end && *json >= '0' && *json <= '9')
+ ++json;
+ }
+
+ if (json >= end) {
+ lastError = QJsonParseError::TerminationByNumber;
+ return false;
+ }
+
+ QByteArray number(start, json - start);
+ DEBUG << "numberstring" << number;
+
+ if (isInt) {
+ bool ok;
+ int n = number.toInt(&ok);
+ if (ok && n < (1<<25) && n > -(1<<25)) {
+ val->int_value = n;
+ val->latinOrIntValue = true;
+ END;
+ return true;
+ }
+ }
+
+ bool ok;
+ union {
+ quint64 ui;
+ double d;
+ };
+ d = number.toDouble(&ok);
+
+ if (!ok) {
+ lastError = QJsonParseError::IllegalNumber;
+ return false;
+ }
+
+ int pos = reserveSpace(sizeof(double));
+ if (pos < 0)
+ return false;
+ qToLittleEndian(ui, data + pos);
+ if (current - baseOffset >= Value::MaxSize) {
+ lastError = QJsonParseError::DocumentTooLarge;
+ return false;
+ }
+ val->value = pos - baseOffset;
+ val->latinOrIntValue = false;
+
+ END;
+ return true;
+}
+
+/*
+
+ string = quotation-mark *char quotation-mark
+
+ char = unescaped /
+ escape (
+ %x22 / ; " quotation mark U+0022
+ %x5C / ; \ reverse solidus U+005C
+ %x2F / ; / solidus U+002F
+ %x62 / ; b backspace U+0008
+ %x66 / ; f form feed U+000C
+ %x6E / ; n line feed U+000A
+ %x72 / ; r carriage return U+000D
+ %x74 / ; t tab U+0009
+ %x75 4HEXDIG ) ; uXXXX U+XXXX
+
+ escape = %x5C ; \
+
+ quotation-mark = %x22 ; "
+
+ unescaped = %x20-21 / %x23-5B / %x5D-10FFFF
+ */
+static inline bool addHexDigit(char digit, uint *result)
+{
+ *result <<= 4;
+ if (digit >= '0' && digit <= '9')
+ *result |= (digit - '0');
+ else if (digit >= 'a' && digit <= 'f')
+ *result |= (digit - 'a') + 10;
+ else if (digit >= 'A' && digit <= 'F')
+ *result |= (digit - 'A') + 10;
+ else
+ return false;
+ return true;
+}
+
+static inline bool scanEscapeSequence(const char *&json, const char *end, uint *ch)
+{
+ ++json;
+ if (json >= end)
+ return false;
+
+ DEBUG << "scan escape" << (char)*json;
+ uint escaped = *json++;
+ switch (escaped) {
+ case '"':
+ *ch = '"'; break;
+ case '\\':
+ *ch = '\\'; break;
+ case '/':
+ *ch = '/'; break;
+ case 'b':
+ *ch = 0x8; break;
+ case 'f':
+ *ch = 0xc; break;
+ case 'n':
+ *ch = 0xa; break;
+ case 'r':
+ *ch = 0xd; break;
+ case 't':
+ *ch = 0x9; break;
+ case 'u': {
+ *ch = 0;
+ if (json > end - 4)
+ return false;
+ for (int i = 0; i < 4; ++i) {
+ if (!addHexDigit(*json, ch))
+ return false;
+ ++json;
+ }
+ return true;
+ }
+ default:
+ // this is not as strict as one could be, but allows for more Json files
+ // to be parsed correctly.
+ *ch = escaped;
+ return true;
+ }
+ return true;
+}
+
+static inline bool scanUtf8Char(const char *&json, const char *end, uint *result)
+{
+ const uchar *&src = reinterpret_cast<const uchar *&>(json);
+ const uchar *uend = reinterpret_cast<const uchar *>(end);
+ uchar b = *src++;
+ int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, result, src, uend);
+ if (res < 0) {
+ // decoding error, backtrack the character we read above
+ --json;
+ return false;
+ }
+
+ return true;
+}
+
+bool Parser::parseString(bool *latin1)
+{
+ *latin1 = true;
+
+ const char *start = json;
+ int outStart = current;
+
+ // try to write out a latin1 string
+
+ int stringPos = reserveSpace(2);
+ if (stringPos < 0)
+ return false;
+
+ BEGIN << "parse string stringPos=" << stringPos << json;
+ while (json < end) {
+ uint ch = 0;
+ if (*json == '"')
+ break;
+ else if (*json == '\\') {
+ if (!scanEscapeSequence(json, end, &ch)) {
+ lastError = QJsonParseError::IllegalEscapeSequence;
+ return false;
+ }
+ } else {
+ if (!scanUtf8Char(json, end, &ch)) {
+ lastError = QJsonParseError::IllegalUTF8String;
+ return false;
+ }
+ }
+ // bail out if the string is not pure latin1 or too long to hold as a latin1string (which has only 16 bit for the length)
+ if (ch > 0xff || json - start >= 0x8000) {
+ *latin1 = false;
+ break;
+ }
+ int pos = reserveSpace(1);
+ if (pos < 0)
+ return false;
+ DEBUG << " " << ch << (char)ch;
+ data[pos] = (uchar)ch;
+ }
+ ++json;
+ DEBUG << "end of string";
+ if (json >= end) {
+ lastError = QJsonParseError::UnterminatedString;
+ return false;
+ }
+
+ // no unicode string, we are done
+ if (*latin1) {
+ // write string length
+ *(QJsonPrivate::qle_ushort *)(data + stringPos) = ushort(current - outStart - sizeof(ushort));
+ int pos = reserveSpace((4 - current) & 3);
+ if (pos < 0)
+ return false;
+ while (pos & 3)
+ data[pos++] = 0;
+ END;
+ return true;
+ }
+
+ *latin1 = false;
+ DEBUG << "not latin";
+
+ json = start;
+ current = outStart + sizeof(int);
+
+ while (json < end) {
+ uint ch = 0;
+ if (*json == '"')
+ break;
+ else if (*json == '\\') {
+ if (!scanEscapeSequence(json, end, &ch)) {
+ lastError = QJsonParseError::IllegalEscapeSequence;
+ return false;
+ }
+ } else {
+ if (!scanUtf8Char(json, end, &ch)) {
+ lastError = QJsonParseError::IllegalUTF8String;
+ return false;
+ }
+ }
+ if (QChar::requiresSurrogates(ch)) {
+ int pos = reserveSpace(4);
+ if (pos < 0)
+ return false;
+ *(QJsonPrivate::qle_ushort *)(data + pos) = QChar::highSurrogate(ch);
+ *(QJsonPrivate::qle_ushort *)(data + pos + 2) = QChar::lowSurrogate(ch);
+ } else {
+ int pos = reserveSpace(2);
+ if (pos < 0)
+ return false;
+ *(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch;
+ }
+ }
+ ++json;
+
+ if (json >= end) {
+ lastError = QJsonParseError::UnterminatedString;
+ return false;
+ }
+
+ // write string length
+ *(QJsonPrivate::qle_int *)(data + stringPos) = (current - outStart - sizeof(int))/2;
+ int pos = reserveSpace((4 - current) & 3);
+ if (pos < 0)
+ return false;
+ while (pos & 3)
+ data[pos++] = 0;
+ END;
+ return true;
+}
+
+QT_END_NAMESPACE