diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-12-12 18:32:19 -0800 |
---|---|---|
committer | Thiago Macieira <thiago.macieira@intel.com> | 2018-01-26 20:59:50 +0000 |
commit | a6b697ca13945a174cff9f3e9b1af1cf61c0bea5 (patch) | |
tree | 78d5fad97ff8e89f079df29cc5a75b4e7197bfc7 /src/corelib/serialization/qjsonparser.cpp | |
parent | 657894624521b580f59ff5f58b9c0e9be159dc1c (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.cpp | 1027 |
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 |