From 35adb74ddd915831789f0175423660f8e898942e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 19 Jun 2019 13:43:00 +0200 Subject: Reimplement JSON support on top of Cbor In turn, deprecate the QJsonDocument methods that deal with JSON binary data. You should use CBOR for data serialization these days. [ChangeLog][Deprecation Notice] The binary JSON representation is deprecated. The CBOR format should be used instead. Fixes: QTBUG-47629 Change-Id: Ic8b92ea36de87815b12307a9d8b1095f07166db8 Reviewed-by: Thiago Macieira --- qmake/Makefile.unix | 10 +- qmake/Makefile.win32 | 3 +- qmake/qmake.pro | 5 +- src/corelib/configure.json | 6 + src/corelib/global/qconfig-bootstrapped.h | 1 + src/corelib/serialization/qbinaryjson.cpp | 415 +++++++++++++ src/corelib/serialization/qbinaryjson_p.h | 617 ++++++++++++++++++ src/corelib/serialization/qbinaryjsonarray.cpp | 137 ++++ src/corelib/serialization/qbinaryjsonarray_p.h | 98 +++ src/corelib/serialization/qbinaryjsonobject.cpp | 149 +++++ src/corelib/serialization/qbinaryjsonobject_p.h | 97 +++ src/corelib/serialization/qbinaryjsonvalue.cpp | 155 +++++ src/corelib/serialization/qbinaryjsonvalue_p.h | 134 ++++ src/corelib/serialization/qcborvalue.cpp | 2 + src/corelib/serialization/qcborvalue.h | 4 + src/corelib/serialization/qjson.cpp | 451 -------------- src/corelib/serialization/qjson_p.h | 789 ++++-------------------- src/corelib/serialization/qjsonarray.cpp | 239 ++----- src/corelib/serialization/qjsonarray.h | 26 +- src/corelib/serialization/qjsoncbor.cpp | 134 ++-- src/corelib/serialization/qjsondocument.cpp | 354 ++++++----- src/corelib/serialization/qjsondocument.h | 27 +- src/corelib/serialization/qjsonobject.cpp | 320 ++++------ src/corelib/serialization/qjsonobject.h | 29 +- src/corelib/serialization/qjsonparser.cpp | 453 ++++++-------- src/corelib/serialization/qjsonparser_p.h | 51 +- src/corelib/serialization/qjsonvalue.cpp | 291 ++++----- src/corelib/serialization/qjsonvalue.h | 54 +- src/corelib/serialization/qjsonwriter.cpp | 75 ++- src/corelib/serialization/qjsonwriter_p.h | 4 +- src/corelib/serialization/serialization.pri | 15 +- src/tools/bootstrap/bootstrap.pro | 3 +- 32 files changed, 2822 insertions(+), 2326 deletions(-) create mode 100644 src/corelib/serialization/qbinaryjson.cpp create mode 100644 src/corelib/serialization/qbinaryjson_p.h create mode 100644 src/corelib/serialization/qbinaryjsonarray.cpp create mode 100644 src/corelib/serialization/qbinaryjsonarray_p.h create mode 100644 src/corelib/serialization/qbinaryjsonobject.cpp create mode 100644 src/corelib/serialization/qbinaryjsonobject_p.h create mode 100644 src/corelib/serialization/qbinaryjsonvalue.cpp create mode 100644 src/corelib/serialization/qbinaryjsonvalue_p.h delete mode 100644 src/corelib/serialization/qjson.cpp diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index 4d4f05e78a..da0fccb834 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -24,7 +24,7 @@ QOBJS = \ qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \ qfilesystementry.o qfsfileengine.o qfsfileengine_iterator.o \ qiodevice.o qsettings.o qtemporaryfile.o qtextstream.o \ - qjsonarray.o qjson.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \ + qcborvalue.o qjsoncbor.o qjsonarray.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \ qmetatype.o qsystemerror.o qvariant.o \ quuid.o \ qarraydata.o qbitarray.o qbytearray.o qbytearraylist.o qbytearraymatcher.o \ @@ -96,9 +96,10 @@ DEPEND_SRC = \ $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp \ $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp \ + $(SOURCE_PATH)/src/corelib/serialization/qcborvalue.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonarray.cpp \ - $(SOURCE_PATH)/src/corelib/serialization/qjson.cpp \ + $(SOURCE_PATH)/src/corelib/serialization/qjsoncbor.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonobject.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonparser.cpp \ @@ -466,7 +467,10 @@ qsystemlibrary.o: $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp qdatastream.o: $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< -qjson.o: $(SOURCE_PATH)/src/corelib/serialization/qjson.cpp +qcborvalue.o: $(SOURCE_PATH)/src/corelib/serialization/qcborvalue.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $< + +qjsoncbor.o: $(SOURCE_PATH)/src/corelib/serialization/qjsoncbor.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< qjsondocument.o: $(SOURCE_PATH)/src/corelib/serialization/qjsondocument.cpp diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index 7324817af2..74fb80e337 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -118,7 +118,8 @@ QTOBJS= \ qxmlutils.obj \ qnumeric.obj \ qlogging.obj \ - qjson.obj \ + qcborvalue.obj \ + qjsoncbor.obj \ qjsondocument.obj \ qjsonparser.obj \ qjsonarray.obj \ diff --git a/qmake/qmake.pro b/qmake/qmake.pro index a9d8b58da8..42c727b33e 100644 --- a/qmake/qmake.pro +++ b/qmake/qmake.pro @@ -116,6 +116,7 @@ SOURCES += \ qbytearray.cpp \ qbytearraymatcher.cpp \ qcalendar.cpp \ + qcborvalue.cpp \ qcryptographichash.cpp \ qdatetime.cpp \ qdir.cpp \ @@ -131,8 +132,8 @@ SOURCES += \ qgregoriancalendar.cpp \ qhash.cpp \ qiodevice.cpp \ - qjson.cpp \ qjsonarray.cpp \ + qjsoncbor.cpp \ qjsondocument.cpp \ qjsonobject.cpp \ qjsonparser.cpp \ @@ -174,6 +175,8 @@ HEADERS += \ qcalendar.h \ qcalendarbackend_p.h \ qcalendarmath_p.h \ + qcborvalue.h \ + qcborvalue_p.h \ qchar.h \ qcryptographichash.h \ qdatetime.h \ diff --git a/src/corelib/configure.json b/src/corelib/configure.json index b4b7c4eec3..04643aec33 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -1094,6 +1094,12 @@ Mozilla License) is included. The data is then also used in QNetworkCookieJar::v Note that this is required for plugin loading. Qt GUI needs QPA plugins for basic operation.", "section": "Utilities", "output": [ "publicFeature" ] + }, + "binaryjson": { + "label": "Binary JSON (deprecated)", + "purpose": "Provides support for the deprecated binary JSON format.", + "section": "Utilities", + "output": [ "publicFeature" ] } }, diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index e9383ca68b..81bf6f95ce 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -74,6 +74,7 @@ #else # define QT_FEATURE_alloca_malloc_h -1 #endif +#define QT_FEATURE_binaryjson -1 #define QT_FEATURE_cborstream -1 #define QT_CRYPTOGRAPHICHASH_ONLY_SHA1 #define QT_FEATURE_cxx11_random (QT_HAS_INCLUDE() ? 1 : -1) diff --git a/src/corelib/serialization/qbinaryjson.cpp b/src/corelib/serialization/qbinaryjson.cpp new file mode 100644 index 0000000000..3d359f0998 --- /dev/null +++ b/src/corelib/serialization/qbinaryjson.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#include "qbinaryjson_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QBinaryJsonPrivate { + +static Q_CONSTEXPR Base emptyArray = { + { qle_uint(sizeof(Base)) }, + { 0 }, + { qle_uint(0) } +}; + +static Q_CONSTEXPR Base emptyObject = { + { qle_uint(sizeof(Base)) }, + { qToLittleEndian(1U) }, + { qle_uint(0) } +}; + +void MutableData::compact() +{ + Q_STATIC_ASSERT(sizeof(Value) == sizeof(offset)); + + Base *base = header->root(); + int reserve = 0; + if (base->is_object) { + auto *o = static_cast(base); + for (uint i = 0; i < o->length; ++i) + reserve += o->entryAt(i)->usedStorage(o); + } else { + auto *a = static_cast(base); + for (uint i = 0; i < a->length; ++i) + reserve += a->at(i)->usedStorage(a); + } + + uint size = sizeof(Base) + reserve + base->length * sizeof(offset); + uint alloc = sizeof(Header) + size; + auto *h = reinterpret_cast
(malloc(alloc)); + Q_CHECK_PTR(h); + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1; + Base *b = h->root(); + b->size = size; + b->is_object = header->root()->is_object; + b->length = base->length; + b->tableOffset = reserve + sizeof(Array); + + uint offset = sizeof(Base); + if (b->is_object) { + const auto *o = static_cast(base); + auto *no = static_cast(b); + + for (uint i = 0; i < o->length; ++i) { + no->table()[i] = offset; + + const Entry *e = o->entryAt(i); + Entry *ne = no->entryAt(i); + uint s = e->size(); + memcpy(ne, e, s); + offset += s; + uint dataSize = e->value.usedStorage(o); + if (dataSize) { + memcpy(reinterpret_cast(no) + offset, e->value.data(o), dataSize); + ne->value.value = offset; + offset += dataSize; + } + } + } else { + const auto *a = static_cast(base); + auto *na = static_cast(b); + + for (uint i = 0; i < a->length; ++i) { + const Value *v = a->at(i); + Value *nv = na->at(i); + *nv = *v; + uint dataSize = v->usedStorage(a); + if (dataSize) { + memcpy(reinterpret_cast(na) + offset, v->data(a), dataSize); + nv->value = offset; + offset += dataSize; + } + } + } + Q_ASSERT(offset == uint(b->tableOffset)); + + free(header); + header = h; + this->alloc = alloc; + compactionCounter = 0; +} + +bool ConstData::isValid() const +{ + if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1U) + return false; + + const Base *root = header->root(); + const uint maxSize = alloc - sizeof(Header); + return root->is_object + ? static_cast(root)->isValid(maxSize) + : static_cast(root)->isValid(maxSize); +} + +QJsonDocument ConstData::toJsonDocument() const +{ + const Base *root = header->root(); + return root->is_object + ? QJsonDocument(static_cast(root)->toJsonObject()) + : QJsonDocument(static_cast(root)->toJsonArray()); +} + +uint Base::reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace) +{ + Q_ASSERT(posInTable <= length); + if (size + dataSize >= Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure %d %d %d", + uint(size), dataSize, Value::MaxSize); + return 0; + } + + offset off = tableOffset; + // move table to new position + if (replace) { + memmove(reinterpret_cast(table()) + dataSize, table(), length * sizeof(offset)); + } else { + memmove(reinterpret_cast(table() + posInTable + numItems) + dataSize, + table() + posInTable, (length - posInTable) * sizeof(offset)); + memmove(reinterpret_cast(table()) + dataSize, table(), posInTable * sizeof(offset)); + } + tableOffset += dataSize; + for (uint i = 0; i < numItems; ++i) + table()[posInTable + i] = off; + size += dataSize; + if (!replace) { + length += numItems; + size += numItems * sizeof(offset); + } + return off; +} + +uint Object::indexOf(QStringView key, bool *exists) const +{ + uint min = 0; + uint n = length; + while (n > 0) { + uint half = n >> 1; + uint middle = min + half; + if (*entryAt(middle) >= key) { + n = half; + } else { + min = middle + 1; + n -= half + 1; + } + } + if (min < length && *entryAt(min) == key) { + *exists = true; + return min; + } + *exists = false; + return min; +} + +QJsonObject Object::toJsonObject() const +{ + QJsonObject object; + for (uint i = 0; i < length; ++i) { + const Entry *e = entryAt(i); + object.insert(e->key(), e->value.toJsonValue(this)); + } + return object; +} + +bool Object::isValid(uint maxSize) const +{ + if (size > maxSize || tableOffset + length * sizeof(offset) > size) + return false; + + QString lastKey; + for (uint i = 0; i < length; ++i) { + if (table()[i] + sizeof(Entry) >= tableOffset) + return false; + const Entry *e = entryAt(i); + if (!e->isValid(tableOffset - table()[i])) + return false; + const QString key = e->key(); + if (key < lastKey) + return false; + if (!e->value.isValid(this)) + return false; + lastKey = key; + } + return true; +} + +QJsonArray Array::toJsonArray() const +{ + QJsonArray array; + const offset *values = table(); + for (uint i = 0; i < length; ++i) + array.append(reinterpret_cast(values + i)->toJsonValue(this)); + return array; +} + +bool Array::isValid(uint maxSize) const +{ + if (size > maxSize || tableOffset + length * sizeof(offset) > size) + return false; + + const offset *values = table(); + for (uint i = 0; i < length; ++i) { + if (!reinterpret_cast(values + i)->isValid(this)) + return false; + } + return true; +} + +uint Value::usedStorage(const Base *b) const +{ + uint s = 0; + switch (type) { + case QJsonValue::Double: + if (!latinOrIntValue) + s = sizeof(double); + break; + case QJsonValue::String: { + const char *d = data(b); + s = latinOrIntValue + ? (sizeof(ushort) + + qFromLittleEndian(*reinterpret_cast(d))) + : (sizeof(int) + + sizeof(ushort) * qFromLittleEndian(*reinterpret_cast(d))); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: + s = base(b)->size; + break; + case QJsonValue::Null: + case QJsonValue::Bool: + default: + break; + } + return alignedSize(s); +} + +QJsonValue Value::toJsonValue(const Base *b) const +{ + switch (type) { + case QJsonValue::Null: + return QJsonValue(QJsonValue::Null); + case QJsonValue::Bool: + return QJsonValue(toBoolean()); + case QJsonValue::Double: + return QJsonValue(toDouble(b)); + case QJsonValue::String: + return QJsonValue(toString(b)); + case QJsonValue::Array: + return static_cast(base(b))->toJsonArray(); + case QJsonValue::Object: + return static_cast(base(b))->toJsonObject(); + case QJsonValue::Undefined: + return QJsonValue(QJsonValue::Undefined); + } + Q_UNREACHABLE(); + return QJsonValue(QJsonValue::Undefined); +} + +inline bool isValidValueOffset(uint offset, uint tableOffset) +{ + return offset >= sizeof(Base) + && offset + sizeof(uint) <= tableOffset; +} + +bool Value::isValid(const Base *b) const +{ + switch (type) { + case QJsonValue::Null: + case QJsonValue::Bool: + return true; + case QJsonValue::Double: + return latinOrIntValue || isValidValueOffset(value, b->tableOffset); + case QJsonValue::String: + if (!isValidValueOffset(value, b->tableOffset)) + return false; + if (latinOrIntValue) + return asLatin1String(b).isValid(b->tableOffset - value); + return asString(b).isValid(b->tableOffset - value); + case QJsonValue::Array: + return isValidValueOffset(value, b->tableOffset) + && static_cast(base(b))->isValid(b->tableOffset - value); + case QJsonValue::Object: + return isValidValueOffset(value, b->tableOffset) + && static_cast(base(b))->isValid(b->tableOffset - value); + default: + return false; + } +} + +uint Value::requiredStorage(const QBinaryJsonValue &v, bool *compressed) +{ + *compressed = false; + switch (v.type()) { + case QJsonValue::Double: + if (QBinaryJsonPrivate::compressedNumber(v.toDouble()) != INT_MAX) { + *compressed = true; + return 0; + } + return sizeof(double); + case QJsonValue::String: { + QString s = v.toString(); + *compressed = QBinaryJsonPrivate::useCompressed(s); + return QBinaryJsonPrivate::qStringSize(s, *compressed); + } + case QJsonValue::Array: + case QJsonValue::Object: + return v.base ? uint(v.base->size) : sizeof(QBinaryJsonPrivate::Base); + case QJsonValue::Undefined: + case QJsonValue::Null: + case QJsonValue::Bool: + break; + } + return 0; +} + +uint Value::valueToStore(const QBinaryJsonValue &v, uint offset) +{ + switch (v.type()) { + case QJsonValue::Undefined: + case QJsonValue::Null: + break; + case QJsonValue::Bool: + return v.toBool(); + case QJsonValue::Double: { + int c = QBinaryJsonPrivate::compressedNumber(v.toDouble()); + if (c != INT_MAX) + return c; + } + Q_FALLTHROUGH(); + case QJsonValue::String: + case QJsonValue::Array: + case QJsonValue::Object: + return offset; + } + return 0; +} + +void Value::copyData(const QBinaryJsonValue &v, char *dest, bool compressed) +{ + switch (v.type()) { + case QJsonValue::Double: + if (!compressed) + qToLittleEndian(v.toDouble(), dest); + break; + case QJsonValue::String: { + const QString str = v.toString(); + QBinaryJsonPrivate::copyString(dest, str, compressed); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: { + const QBinaryJsonPrivate::Base *b = v.base; + if (!b) + b = (v.type() == QJsonValue::Array ? &emptyArray : &emptyObject); + memcpy(dest, b, b->size); + break; + } + default: + break; + } +} + +} // namespace QBinaryJsonPrivate + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qbinaryjson_p.h b/src/corelib/serialization/qbinaryjson_p.h new file mode 100644 index 0000000000..132c36f227 --- /dev/null +++ b/src/corelib/serialization/qbinaryjson_p.h @@ -0,0 +1,617 @@ +/**************************************************************************** +** +** 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 QBINARYJSON_P_H +#define QBINARYJSON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +#include + +QT_REQUIRE_CONFIG(binaryjson); + +QT_BEGIN_NAMESPACE + +// in qstring.cpp +void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len); +void qt_from_latin1(ushort *dst, const char *str, size_t size) noexcept; + +/* + This defines a binary data structure for Json data. The data structure is optimised for fast reading + and minimum allocations. The whole data structure can be mmap'ed and used directly. + + In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but + much faster to access. + + The size requirements are: + + String: + Latin1 data: 2 bytes header + string.length() + Full Unicode: 4 bytes header + 2*(string.length()) + + Values: 4 bytes + size of data (size can be 0 for some data) + bool: 0 bytes + double: 8 bytes (0 if integer with less than 27bits) + string: see above + array: size of array + object: size of object + Array: 12 bytes + 4*length + size of Value data + Object: 12 bytes + 8*length + size of Key Strings + size of Value data + + For an example such as + + { // object: 12 + 5*8 = 52 + "firstName": "John", // key 12, value 8 = 20 + "lastName" : "Smith", // key 12, value 8 = 20 + "age" : 25, // key 8, value 0 = 8 + "address" : // key 12, object below = 140 + { // object: 12 + 4*8 + "streetAddress": "21 2nd Street", // key 16, value 16 + "city" : "New York", // key 8, value 12 + "state" : "NY", // key 8, value 4 + "postalCode" : "10021" // key 12, value 8 + }, // object total: 128 + "phoneNumber": // key: 16, value array below = 172 + [ // array: 12 + 2*4 + values below: 156 + { // object 12 + 2*8 + "type" : "home", // key 8, value 8 + "number": "212 555-1234" // key 8, value 16 + }, // object total: 68 + { // object 12 + 2*8 + "type" : "fax", // key 8, value 8 + "number": "646 555-4567" // key 8, value 16 + } // object total: 68 + ] // array total: 156 + } // great total: 412 bytes + + The uncompressed text file used roughly 500 bytes, so in this case we end up using about + the same space as the text representation. + + Other measurements have shown a slightly bigger binary size than a compact text + representation where all possible whitespace was stripped out. +*/ +namespace QBinaryJsonPrivate { + +class Array; +class Object; +class Value; +class Entry; + +template +using q_littleendian = QLEInteger; + +using qle_short = q_littleendian; +using qle_ushort = q_littleendian; +using qle_int = q_littleendian; +using qle_uint = q_littleendian; + +template +using qle_bitfield = QLEIntegerBitfield; + +template +using qle_signedbitfield = QLEIntegerBitfield; + +using offset = qle_uint; + +// round the size up to the next 4 byte boundary +inline uint alignedSize(uint size) { return (size + 3) & ~3; } + +const int MaxLatin1Length = 0x7fff; + +static inline bool useCompressed(QStringView s) +{ + if (s.length() > MaxLatin1Length) + return false; + return QtPrivate::isLatin1(s); +} + +static inline bool useCompressed(QLatin1String s) +{ + return s.size() <= MaxLatin1Length; +} + +static inline uint qStringSize(const QString &string, bool compress) +{ + uint l = 2 + string.size(); + if (!compress) + l *= 2; + return alignedSize(l); +} + +// returns INT_MAX if it can't compress it into 28 bits +static inline int compressedNumber(double d) +{ + // this relies on details of how ieee floats are represented + const int exponent_off = 52; + const quint64 fraction_mask = 0x000fffffffffffffULL; + const quint64 exponent_mask = 0x7ff0000000000000ULL; + + quint64 val; + memcpy (&val, &d, sizeof(double)); + int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; + if (exp < 0 || exp > 25) + return std::numeric_limits::max(); + + quint64 non_int = val & (fraction_mask >> exp); + if (non_int) + return std::numeric_limits::max(); + + bool neg = (val >> 63) != 0; + val &= fraction_mask; + val |= ((quint64)1 << 52); + int res = (int)(val >> (52 - exp)); + return neg ? -res : res; +} + +class Latin1String; + +class String +{ +public: + explicit String(const char *data) : d(reinterpret_cast(data)) {} + + struct Data { + qle_uint length; + qle_ushort utf16[1]; + }; + const Data *d; + + uint byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; } + bool isValid(uint maxSize) const + { + // Check byteSize() <= maxSize, avoiding integer overflow + return maxSize >= sizeof(uint) + && uint(d->length) <= (maxSize - sizeof(uint)) / sizeof(ushort); + } + + static void copy(char *dest, QStringView str) + { + Data *data = reinterpret_cast(dest); + data->length = str.length(); + qToLittleEndian(str.utf16(), str.length(), data->utf16); + fillTrailingZeros(data); + } + + static void fillTrailingZeros(Data *data) + { + if (data->length & 1) + data->utf16[data->length] = 0; + } + + bool operator ==(QStringView str) const + { + int slen = str.length(); + int l = d->length; + if (slen != l) + return false; + const auto *s = reinterpret_cast(str.utf16()); + const qle_ushort *a = d->utf16; + const ushort *b = s; + while (l-- && *a == *b) + a++,b++; + return (l == -1); + } + + bool operator ==(const String &str) const + { + if (d->length != str.d->length) + return false; + return !memcmp(d->utf16, str.d->utf16, d->length * sizeof(ushort)); + } + + QString toString() const + { +#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN + return QString(reinterpret_cast(d->utf16), d->length); +#else + const uint l = d->length; + QString str(l, Qt::Uninitialized); + QChar *ch = str.data(); + for (uint i = 0; i < l; ++i) + ch[i] = QChar(d->utf16[i]); + return str; +#endif + } +}; + +class Latin1String +{ +public: + explicit Latin1String(const char *data) : d(reinterpret_cast(data)) {} + + struct Data { + qle_ushort length; + char latin1[1]; + }; + const Data *d; + + uint byteSize() const { return sizeof(ushort) + sizeof(char) * (d->length); } + bool isValid(uint maxSize) const { return byteSize() <= maxSize; } + + static void copy(char *dest, QStringView src) + { + Data *data = reinterpret_cast(dest); + data->length = src.length(); + auto *l = reinterpret_cast(data->latin1); + const auto *uc = reinterpret_cast(src.utf16()); + qt_to_latin1_unchecked(l, uc, data->length); + + for (uint len = data->length; quintptr(l + len) & 0x3; ++len) + l[len] = 0; + } + + QLatin1String toQLatin1String() const noexcept { return QLatin1String(d->latin1, d->length); } + QString toString() const { return QString::fromLatin1(d->latin1, d->length); } +}; + +static inline void copyString(char *dest, QStringView str, bool compress) +{ + if (compress) + Latin1String::copy(dest, str); + else + String::copy(dest, str); +} + +/* + Base is the base class for both Object and Array. Both classes work more or less the same way. + The class starts with a header (defined by the struct below), then followed by data (the data for + values in the Array case and Entry's (see below) for objects. + + After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and + offsets from the beginning of the object to Entry's in the case of Object. + + Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage + of a binary search over the keys in an Object. + */ +class Base +{ +public: + qle_uint size; + union { + uint _dummy; + qle_bitfield<0, 1> is_object; + qle_bitfield<1, 31> length; + }; + offset tableOffset; + // content follows here + + bool isObject() const { return !!is_object; } + bool isArray() const { return !isObject(); } + + offset *table() + { + return reinterpret_cast(reinterpret_cast(this) + tableOffset); + } + + const offset *table() const + { + return reinterpret_cast(reinterpret_cast(this) + tableOffset); + } + + uint reserveSpace(uint dataSize, uint posInTable, uint numItems, bool replace); +}; + +class Object : public Base +{ +public: + const Entry *entryAt(uint i) const + { + return reinterpret_cast(reinterpret_cast(this) + table()[i]); + } + + Entry *entryAt(uint i) + { + return reinterpret_cast(reinterpret_cast(this) + table()[i]); + } + + uint indexOf(QStringView key, bool *exists) const; + QJsonObject toJsonObject() const; + bool isValid(uint maxSize) const; +}; + +class Array : public Base +{ +public: + const Value *at(uint i) const { return reinterpret_cast(table() + i); } + Value *at(uint i) { return reinterpret_cast(table() + i); } + + QJsonArray toJsonArray() const; + bool isValid(uint maxSize) const; +}; + +class Value +{ +public: + enum { + MaxSize = (1 << 27) - 1 + }; + union { + uint _dummy; + qle_bitfield<0, 3> type; + qle_bitfield<3, 1> latinOrIntValue; + qle_bitfield<4, 1> latinKey; + qle_bitfield<5, 27> value; + qle_signedbitfield<5, 27> int_value; + }; + + inline const char *data(const Base *b) const + { + return reinterpret_cast(b) + value; + } + + uint usedStorage(const Base *b) const; + + bool toBoolean() const + { + Q_ASSERT(type == QJsonValue::Bool); + return value != 0; + } + + double toDouble(const Base *b) const + { + Q_ASSERT(type == QJsonValue::Double); + if (latinOrIntValue) + return int_value; + + auto i = qFromLittleEndian(reinterpret_cast(b) + value); + double d; + memcpy(&d, &i, sizeof(double)); + return d; + } + + QString toString(const Base *b) const + { + return latinOrIntValue + ? asLatin1String(b).toString() + : asString(b).toString(); + } + + String asString(const Base *b) const + { + Q_ASSERT(type == QJsonValue::String && !latinOrIntValue); + return String(data(b)); + } + + Latin1String asLatin1String(const Base *b) const + { + Q_ASSERT(type == QJsonValue::String && latinOrIntValue); + return Latin1String(data(b)); + } + + const Base *base(const Base *b) const + { + Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object); + return reinterpret_cast(data(b)); + } + + QJsonValue toJsonValue(const Base *b) const; + bool isValid(const Base *b) const; + + static uint requiredStorage(const QBinaryJsonValue &v, bool *compressed); + static uint valueToStore(const QBinaryJsonValue &v, uint offset); + static void copyData(const QBinaryJsonValue &v, char *dest, bool compressed); +}; + +class Entry { +public: + Value value; + // key + // value data follows key + + uint size() const + { + uint s = sizeof(Entry); + if (value.latinKey) + s += shallowLatin1Key().byteSize(); + else + s += shallowKey().byteSize(); + return alignedSize(s); + } + + uint usedStorage(Base *b) const + { + return size() + value.usedStorage(b); + } + + String shallowKey() const + { + Q_ASSERT(!value.latinKey); + return String(reinterpret_cast(this) + sizeof(Entry)); + } + + Latin1String shallowLatin1Key() const + { + Q_ASSERT(value.latinKey); + return Latin1String(reinterpret_cast(this) + sizeof(Entry)); + } + + QString key() const + { + return value.latinKey + ? shallowLatin1Key().toString() + : shallowKey().toString(); + } + + bool isValid(uint maxSize) const + { + if (maxSize < sizeof(Entry)) + return false; + maxSize -= sizeof(Entry); + return value.latinKey + ? shallowLatin1Key().isValid(maxSize) + : shallowKey().isValid(maxSize); + } + + bool operator ==(QStringView key) const + { + return value.latinKey + ? (shallowLatin1Key().toQLatin1String() == key) + : (shallowKey() == key); + } + + bool operator >=(QStringView key) const + { + return value.latinKey + ? (shallowLatin1Key().toQLatin1String() >= key) + : (shallowKey().toString() >= key); + } +}; + +class Header { +public: + qle_uint tag; // 'qbjs' + qle_uint version; // 1 + Base *root() { return reinterpret_cast(this + 1); } + const Base *root() const { return reinterpret_cast(this + 1); } +}; + +class ConstData +{ + Q_DISABLE_COPY_MOVE(ConstData) +public: + const uint alloc; + union { + const char *rawData; + const Header *header; + }; + + ConstData(const char *raw, uint a) : alloc(a), rawData(raw) {} + bool isValid() const; + QJsonDocument toJsonDocument() const; +}; + +class MutableData +{ + Q_DISABLE_COPY_MOVE(MutableData) +public: + QAtomicInt ref; + uint alloc; + union { + char *rawData; + Header *header; + }; + uint compactionCounter : 31; + + MutableData(char *raw, uint a) + : alloc(a), rawData(raw), compactionCounter(0) + { + } + + MutableData(uint reserved, QJsonValue::Type valueType) + : rawData(nullptr), compactionCounter(0) + { + Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object); + + alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset); + header = reinterpret_cast
(malloc(alloc)); + Q_CHECK_PTR(header); + header->tag = QJsonDocument::BinaryFormatTag; + header->version = 1; + Base *b = header->root(); + b->size = sizeof(Base); + b->is_object = (valueType == QJsonValue::Object); + b->tableOffset = sizeof(Base); + b->length = 0; + } + + ~MutableData() + { + free(rawData); + } + + MutableData *clone(const Base *b, uint reserve = 0) + { + uint size = sizeof(Header) + b->size; + if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve) + return this; + + if (reserve) { + if (reserve < 128) + reserve = 128; + size = qMax(size + reserve, qMin(size *2, uint(Value::MaxSize))); + if (size > Value::MaxSize) { + qWarning("QJson: Document too large to store in data structure"); + return nullptr; + } + } + char *raw = reinterpret_cast(malloc(size)); + Q_CHECK_PTR(raw); + memcpy(raw + sizeof(Header), b, b->size); + auto *h = reinterpret_cast
(raw); + h->tag = QJsonDocument::BinaryFormatTag; + h->version = 1; + auto *d = new MutableData(raw, size); + d->compactionCounter = (b == header->root()) ? compactionCounter : 0; + return d; + } + + char *takeRawData(uint *size) + { + *size = alloc; + char *result = rawData; + rawData = nullptr; + alloc = 0; + return result; + } + + void compact(); +}; + +} // namespace QBinaryJsonPrivate + +Q_DECLARE_TYPEINFO(QBinaryJsonPrivate::Value, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif // QBINARYJSON_P_H diff --git a/src/corelib/serialization/qbinaryjsonarray.cpp b/src/corelib/serialization/qbinaryjsonarray.cpp new file mode 100644 index 0000000000..68937fe17d --- /dev/null +++ b/src/corelib/serialization/qbinaryjsonarray.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#include "qbinaryjsonarray_p.h" +#include "qbinaryjson_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QBinaryJsonArray::~QBinaryJsonArray() +{ + if (d && !d->ref.deref()) + delete d; +} + +QBinaryJsonArray QBinaryJsonArray::fromJsonArray(const QJsonArray &array) +{ + QBinaryJsonArray binary; + for (const QJsonValue &value : array) + binary.append(QBinaryJsonValue::fromJsonValue(value)); + if (binary.d) // We want to compact it as it is a root item now + binary.d->compactionCounter++; + binary.compact(); + return binary; +} + +void QBinaryJsonArray::append(const QBinaryJsonValue &value) +{ + const uint i = a ? a->length : 0; + + bool compressed; + uint valueSize = QBinaryJsonPrivate::Value::requiredStorage(value, &compressed); + + if (!detach(valueSize + sizeof(QBinaryJsonPrivate::Value))) + return; + + if (!a->length) + a->tableOffset = sizeof(QBinaryJsonPrivate::Array); + + uint valueOffset = a->reserveSpace(valueSize, i, 1, false); + if (!valueOffset) + return; + + QBinaryJsonPrivate::Value *v = a->at(i); + v->type = (value.t == QJsonValue::Undefined ? QJsonValue::Null : value.t); + v->latinOrIntValue = compressed; + v->latinKey = false; + v->value = QBinaryJsonPrivate::Value::valueToStore(value, valueOffset); + if (valueSize) { + QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast(a) + valueOffset, + compressed); + } +} + +char *QBinaryJsonArray::takeRawData(uint *size) +{ + if (d) + return d->takeRawData(size); + *size = 0; + return nullptr; +} + +bool QBinaryJsonArray::detach(uint reserve) +{ + if (!d) { + if (reserve >= QBinaryJsonPrivate::Value::MaxSize) { + qWarning("QBinaryJson: Document too large to store in data structure"); + return false; + } + d = new QBinaryJsonPrivate::MutableData(reserve, QJsonValue::Array); + a = static_cast(d->header->root()); + d->ref.ref(); + return true; + } + if (reserve == 0 && d->ref.loadRelaxed() == 1) + return true; + + QBinaryJsonPrivate::MutableData *x = d->clone(a, reserve); + if (!x) + return false; + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + a = static_cast(d->header->root()); + return true; +} + +void QBinaryJsonArray::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach(); + d->compact(); + a = static_cast(d->header->root()); +} + +QT_END_NAMESPACE + diff --git a/src/corelib/serialization/qbinaryjsonarray_p.h b/src/corelib/serialization/qbinaryjsonarray_p.h new file mode 100644 index 0000000000..2bb8fed387 --- /dev/null +++ b/src/corelib/serialization/qbinaryjsonarray_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** 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 QBINARYJSONARRAY_P_H +#define QBINARYJSONARRAY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qbinaryjsonvalue_p.h" + +QT_REQUIRE_CONFIG(binaryjson); + +QT_BEGIN_NAMESPACE + +class QBinaryJsonArray +{ + Q_DISABLE_COPY(QBinaryJsonArray) +public: + QBinaryJsonArray() = default; + ~QBinaryJsonArray(); + + QBinaryJsonArray(QBinaryJsonArray &&other) noexcept + : d(other.d), + a(other.a) + { + other.d = nullptr; + other.a = nullptr; + } + + QBinaryJsonArray &operator =(QBinaryJsonArray &&other) noexcept + { + qSwap(d, other.d); + qSwap(a, other.a); + return *this; + } + + static QBinaryJsonArray fromJsonArray(const QJsonArray &array); + char *takeRawData(uint *size); + +private: + friend class QBinaryJsonValue; + + void append(const QBinaryJsonValue &value); + void compact(); + bool detach(uint reserve = 0); + + QBinaryJsonPrivate::MutableData *d = nullptr; + QBinaryJsonPrivate::Array *a = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QBINARYJSONARRAY_P_H diff --git a/src/corelib/serialization/qbinaryjsonobject.cpp b/src/corelib/serialization/qbinaryjsonobject.cpp new file mode 100644 index 0000000000..3186ab6087 --- /dev/null +++ b/src/corelib/serialization/qbinaryjsonobject.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#include "qbinaryjsonobject_p.h" +#include "qbinaryjson_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QBinaryJsonObject::~QBinaryJsonObject() +{ + if (d && !d->ref.deref()) + delete d; +} + +QBinaryJsonObject QBinaryJsonObject::fromJsonObject(const QJsonObject &object) +{ + QBinaryJsonObject binary; + for (auto it = object.begin(), end = object.end(); it != end; ++it) + binary.insert(it.key(), QBinaryJsonValue::fromJsonValue(it.value())); + if (binary.d) // We want to compact it as it is a root item now + binary.d->compactionCounter++; + binary.compact(); + return binary; +} + +void QBinaryJsonObject::insert(const QString &key, const QBinaryJsonValue &value) +{ + bool latinOrIntValue; + uint valueSize = QBinaryJsonPrivate::Value::requiredStorage(value, &latinOrIntValue); + + bool latinKey = QBinaryJsonPrivate::useCompressed(key); + uint valueOffset = sizeof(QBinaryJsonPrivate::Entry) + + QBinaryJsonPrivate::qStringSize(key, latinKey); + uint requiredSize = valueOffset + valueSize; + + if (!detach(requiredSize + sizeof(QBinaryJsonPrivate::offset))) // offset for the new index entry + return; + + if (!o->length) + o->tableOffset = sizeof(QBinaryJsonPrivate::Object); + + bool keyExists = false; + uint pos = o->indexOf(key, &keyExists); + if (keyExists) + ++d->compactionCounter; + + uint off = o->reserveSpace(requiredSize, pos, 1, keyExists); + if (!off) + return; + + QBinaryJsonPrivate::Entry *e = o->entryAt(pos); + e->value.type = value.t; + e->value.latinKey = latinKey; + e->value.latinOrIntValue = latinOrIntValue; + e->value.value = QBinaryJsonPrivate::Value::valueToStore( + value, reinterpret_cast(e) - reinterpret_cast(o) + valueOffset); + QBinaryJsonPrivate::copyString(reinterpret_cast(e + 1), key, latinKey); + if (valueSize) { + QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast(e) + valueOffset, + latinOrIntValue); + } + + if (d->compactionCounter > 32U && d->compactionCounter >= unsigned(o->length) / 2U) + compact(); +} + +char *QBinaryJsonObject::takeRawData(uint *size) const +{ + if (d) + return d->takeRawData(size); + *size = 0; + return nullptr; +} + +bool QBinaryJsonObject::detach(uint reserve) +{ + if (!d) { + if (reserve >= QBinaryJsonPrivate::Value::MaxSize) { + qWarning("QBinaryJson: Document too large to store in data structure"); + return false; + } + d = new QBinaryJsonPrivate::MutableData(reserve, QJsonValue::Object); + o = static_cast(d->header->root()); + d->ref.ref(); + return true; + } + if (reserve == 0 && d->ref.loadRelaxed() == 1) + return true; + + QBinaryJsonPrivate::MutableData *x = d->clone(o, reserve); + if (!x) + return false; + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + o = static_cast(d->header->root()); + return true; +} + +void QBinaryJsonObject::compact() +{ + if (!d || !d->compactionCounter) + return; + + detach(); + d->compact(); + o = static_cast(d->header->root()); +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qbinaryjsonobject_p.h b/src/corelib/serialization/qbinaryjsonobject_p.h new file mode 100644 index 0000000000..c0991ac339 --- /dev/null +++ b/src/corelib/serialization/qbinaryjsonobject_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** 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 QBINARYJSONOBJECT_H +#define QBINARYJSONOBJECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qbinaryjsonvalue_p.h" + +QT_REQUIRE_CONFIG(binaryjson); + +QT_BEGIN_NAMESPACE + +class QBinaryJsonObject +{ + Q_DISABLE_COPY(QBinaryJsonObject) +public: + QBinaryJsonObject() = default; + ~QBinaryJsonObject(); + + QBinaryJsonObject(QBinaryJsonObject &&other) noexcept + : d(other.d), o(other.o) + { + other.d = nullptr; + other.o = nullptr; + } + + QBinaryJsonObject &operator =(QBinaryJsonObject &&other) noexcept + { + qSwap(d, other.d); + qSwap(o, other.o); + return *this; + } + + static QBinaryJsonObject fromJsonObject(const QJsonObject &object); + char *takeRawData(uint *size) const; + +private: + friend class QBinaryJsonValue; + + void insert(const QString &key, const QBinaryJsonValue &value); + bool detach(uint reserve = 0); + void compact(); + + QBinaryJsonPrivate::MutableData *d = nullptr; + QBinaryJsonPrivate::Object *o = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QBINARYJSONOBJECT_P_H diff --git a/src/corelib/serialization/qbinaryjsonvalue.cpp b/src/corelib/serialization/qbinaryjsonvalue.cpp new file mode 100644 index 0000000000..5e3a01ad38 --- /dev/null +++ b/src/corelib/serialization/qbinaryjsonvalue.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#include "qbinaryjsonobject_p.h" +#include "qbinaryjsonvalue_p.h" +#include "qbinaryjsonarray_p.h" +#include "qbinaryjson_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +QBinaryJsonValue::QBinaryJsonValue(QBinaryJsonPrivate::MutableData *data, + QBinaryJsonPrivate::Base *parent, + const QBinaryJsonPrivate::Value &v) + : t(QJsonValue::Type(uint(v.type))) +{ + switch (t) { + case QJsonValue::Undefined: + case QJsonValue::Null: + dbl = 0; + break; + case QJsonValue::Bool: + b = v.toBoolean(); + break; + case QJsonValue::Double: + dbl = v.toDouble(parent); + break; + case QJsonValue::String: { + QString s = v.toString(parent); + stringData = s.data_ptr(); + stringData->ref.ref(); + break; + } + case QJsonValue::Array: + case QJsonValue::Object: + d = data; + base = v.base(parent); + break; + } + if (d) + d->ref.ref(); +} + +QBinaryJsonValue::QBinaryJsonValue(QString string) + : stringData(*reinterpret_cast(&string)), t(QJsonValue::String) +{ + stringData->ref.ref(); +} + +QBinaryJsonValue::QBinaryJsonValue(const QBinaryJsonArray &a) + : base(a.a), d(a.d), t(QJsonValue::Array) +{ + if (d) + d->ref.ref(); +} + +QBinaryJsonValue::QBinaryJsonValue(const QBinaryJsonObject &o) + : base(o.o), d(o.d), t(QJsonValue::Object) +{ + if (d) + d->ref.ref(); +} + +QBinaryJsonValue::~QBinaryJsonValue() +{ + if (t == QJsonValue::String && stringData && !stringData->ref.deref()) + free(stringData); + + if (d && !d->ref.deref()) + delete d; +} + +QBinaryJsonValue QBinaryJsonValue::fromJsonValue(const QJsonValue &json) +{ + switch (json.type()) { + case QJsonValue::Bool: + return QBinaryJsonValue(json.toBool()); + case QJsonValue::Double: + return QBinaryJsonValue(json.toDouble()); + case QJsonValue::String: + return QBinaryJsonValue(json.toString()); + case QJsonValue::Array: + return QBinaryJsonArray::fromJsonArray(json.toArray()); + case QJsonValue::Object: + return QBinaryJsonObject::fromJsonObject(json.toObject()); + case QJsonValue::Null: + return QBinaryJsonValue(QJsonValue::Null); + case QJsonValue::Undefined: + return QBinaryJsonValue(QJsonValue::Undefined); + } + Q_UNREACHABLE(); + return QBinaryJsonValue(QJsonValue::Null); +} + +QString QBinaryJsonValue::toString() const +{ + if (t != QJsonValue::String) + return QString(); + stringData->ref.ref(); // the constructor below doesn't add a ref. + QStringDataPtr holder = { stringData }; + return QString(holder); +} + +void QBinaryJsonValue::detach() +{ + if (!d) + return; + + QBinaryJsonPrivate::MutableData *x = d->clone(base); + x->ref.ref(); + if (!d->ref.deref()) + delete d; + d = x; + base = static_cast(d->header->root()); +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qbinaryjsonvalue_p.h b/src/corelib/serialization/qbinaryjsonvalue_p.h new file mode 100644 index 0000000000..498fc62ecd --- /dev/null +++ b/src/corelib/serialization/qbinaryjsonvalue_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** 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 QBINARYJSONVALUE_P_H +#define QBINARYJSONVALUE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_REQUIRE_CONFIG(binaryjson); + +QT_BEGIN_NAMESPACE + +class QBinaryJsonArray; +class QBinaryJsonObject; + +namespace QBinaryJsonPrivate { +class ConstData; +class MutableData; +class Base; +class Value; +class Object; +class Array; +} + +class Q_CORE_EXPORT QBinaryJsonValue +{ + Q_DISABLE_COPY(QBinaryJsonValue) +public: + explicit QBinaryJsonValue(QJsonValue::Type type) : ui(0), t(type) {} + explicit QBinaryJsonValue(bool b) : b(b), t(QJsonValue::Bool) {} + explicit QBinaryJsonValue(double n) : dbl(n), t(QJsonValue::Double) {} + explicit QBinaryJsonValue(QString s); + QBinaryJsonValue(const QBinaryJsonArray &a); + QBinaryJsonValue(const QBinaryJsonObject &o); + + ~QBinaryJsonValue(); + + QBinaryJsonValue(QBinaryJsonValue &&other) noexcept + : ui(other.ui), + d(other.d), + t(other.t) + { + other.ui = 0; + other.d = nullptr; + other.t = QJsonValue::Null; + } + + QBinaryJsonValue &operator =(QBinaryJsonValue &&other) noexcept + { + qSwap(ui, other.ui); + qSwap(d, other.d); + qSwap(t, other.t); + return *this; + } + + static QBinaryJsonValue fromJsonValue(const QJsonValue &json); + QJsonValue::Type type() const { return t; } + bool toBool() const { return (t == QJsonValue::Bool) && b; } + double toDouble() const { return (t == QJsonValue::Double) ? dbl : 0; } + QString toString() const; + +private: + friend class QBinaryJsonPrivate::Value; + friend class QBinaryJsonArray; + friend class QBinaryJsonObject; + + QBinaryJsonValue(QBinaryJsonPrivate::MutableData *d, QBinaryJsonPrivate::Base *parent, + const QBinaryJsonPrivate::Value &v); + + void detach(); + + union { + quint64 ui; + bool b; + double dbl; + QStringData *stringData; + const QBinaryJsonPrivate::Base *base; + }; + QBinaryJsonPrivate::MutableData *d = nullptr; // needed for Objects and Arrays + QJsonValue::Type t = QJsonValue::Null; +}; + +QT_END_NAMESPACE + +#endif // QBINARYJSONVALUE_P_H diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 23595f4092..c2f670de06 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -3109,4 +3109,6 @@ QT_END_NAMESPACE #include "qcborarray.cpp" #include "qcbormap.cpp" +#ifndef QT_NO_QOBJECT #include "moc_qcborvalue.cpp" +#endif diff --git a/src/corelib/serialization/qcborvalue.h b/src/corelib/serialization/qcborvalue.h index accd0fae8a..9c613dfcfb 100644 --- a/src/corelib/serialization/qcborvalue.h +++ b/src/corelib/serialization/qcborvalue.h @@ -71,6 +71,8 @@ class QCborStreamReader; class QCborStreamWriter; class QDataStream; +namespace QJsonPrivate { class Value; } + struct QCborParserError { qint64 offset = 0; @@ -301,6 +303,8 @@ public: private: friend class QCborValueRef; friend class QCborContainerPrivate; + friend class QJsonPrivate::Value; + qint64 n = 0; QCborContainerPrivate *container = nullptr; Type t = Undefined; diff --git a/src/corelib/serialization/qjson.cpp b/src/corelib/serialization/qjson.cpp deleted file mode 100644 index 76f1eae1ce..0000000000 --- a/src/corelib/serialization/qjson.cpp +++ /dev/null @@ -1,451 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** 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$ -** -****************************************************************************/ - -#include "qjson_p.h" -#include - -QT_BEGIN_NAMESPACE - -namespace QJsonPrivate -{ - -static Q_CONSTEXPR Base emptyArray = { { qle_uint(sizeof(Base)) }, { 0 }, { qle_uint(0) } }; -static Q_CONSTEXPR Base emptyObject = { { qle_uint(sizeof(Base)) }, { qToLittleEndian(1u) }, { qle_uint(0) } }; - -void Data::compact() -{ - Q_ASSERT(sizeof(Value) == sizeof(offset)); - - if (!compactionCounter) - return; - - Base *base = header->root(); - int reserve = 0; - if (base->is_object) { - Object *o = static_cast(base); - for (int i = 0; i < (int)o->length; ++i) - reserve += o->entryAt(i)->usedStorage(o); - } else { - Array *a = static_cast(base); - for (int i = 0; i < (int)a->length; ++i) - reserve += (*a)[i].usedStorage(a); - } - - int size = sizeof(Base) + reserve + base->length*sizeof(offset); - int alloc = sizeof(Header) + size; - Header *h = (Header *) malloc(alloc); - Q_CHECK_PTR(h); - h->tag = QJsonDocument::BinaryFormatTag; - h->version = 1; - Base *b = h->root(); - b->size = size; - b->is_object = header->root()->is_object; - b->length = base->length; - b->tableOffset = reserve + sizeof(Array); - - int offset = sizeof(Base); - if (b->is_object) { - Object *o = static_cast(base); - Object *no = static_cast(b); - - for (int i = 0; i < (int)o->length; ++i) { - no->table()[i] = offset; - - const Entry *e = o->entryAt(i); - Entry *ne = no->entryAt(i); - int s = e->size(); - memcpy(ne, e, s); - offset += s; - int dataSize = e->value.usedStorage(o); - if (dataSize) { - memcpy((char *)no + offset, e->value.data(o), dataSize); - ne->value.value = offset; - offset += dataSize; - } - } - } else { - Array *a = static_cast(base); - Array *na = static_cast(b); - - for (int i = 0; i < (int)a->length; ++i) { - const Value &v = (*a)[i]; - Value &nv = (*na)[i]; - nv = v; - int dataSize = v.usedStorage(a); - if (dataSize) { - memcpy((char *)na + offset, v.data(a), dataSize); - nv.value = offset; - offset += dataSize; - } - } - } - Q_ASSERT(offset == (int)b->tableOffset); - - free(header); - header = h; - this->alloc = alloc; - compactionCounter = 0; -} - -bool Data::valid() const -{ - if (header->tag != QJsonDocument::BinaryFormatTag || header->version != 1u) - return false; - - bool res = false; - Base *root = header->root(); - int maxSize = alloc - sizeof(Header); - if (root->is_object) - res = static_cast(root)->isValid(maxSize); - else - res = static_cast(root)->isValid(maxSize); - - return res; -} - - -int Base::reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace) -{ - Q_ASSERT(posInTable >= 0 && posInTable <= (int)length); - if (size + dataSize >= Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure %d %d %d", (uint)size, dataSize, Value::MaxSize); - return 0; - } - - offset off = tableOffset; - // move table to new position - if (replace) { - memmove((char *)(table()) + dataSize, table(), length*sizeof(offset)); - } else { - memmove((char *)(table() + posInTable + numItems) + dataSize, table() + posInTable, (length - posInTable)*sizeof(offset)); - memmove((char *)(table()) + dataSize, table(), posInTable*sizeof(offset)); - } - tableOffset += dataSize; - for (int i = 0; i < (int)numItems; ++i) - table()[posInTable + i] = off; - size += dataSize; - if (!replace) { - length += numItems; - size += numItems * sizeof(offset); - } - return off; -} - -void Base::removeItems(int pos, int numItems) -{ - Q_ASSERT(pos >= 0 && pos <= (int)length); - if (pos + numItems < (int)length) - memmove(table() + pos, table() + pos + numItems, (length - pos - numItems)*sizeof(offset)); - length -= numItems; -} - -int Object::indexOf(QStringView key, bool *exists) const -{ - int min = 0; - int n = length; - while (n > 0) { - int half = n >> 1; - int middle = min + half; - if (*entryAt(middle) >= key) { - n = half; - } else { - min = middle + 1; - n -= half + 1; - } - } - if (min < (int)length && *entryAt(min) == key) { - *exists = true; - return min; - } - *exists = false; - return min; -} - -int Object::indexOf(QLatin1String key, bool *exists) const -{ - int min = 0; - int n = length; - while (n > 0) { - int half = n >> 1; - int middle = min + half; - if (*entryAt(middle) >= key) { - n = half; - } else { - min = middle + 1; - n -= half + 1; - } - } - if (min < (int)length && *entryAt(min) == key) { - *exists = true; - return min; - } - *exists = false; - return min; -} - -bool Object::isValid(int maxSize) const -{ - if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) - return false; - - QString lastKey; - for (uint i = 0; i < length; ++i) { - offset entryOffset = table()[i]; - if (entryOffset + sizeof(Entry) >= tableOffset) - return false; - Entry *e = entryAt(i); - if (!e->isValid(tableOffset - table()[i])) - return false; - QString key = e->key(); - if (key < lastKey) - return false; - if (!e->value.isValid(this)) - return false; - lastKey = key; - } - return true; -} - - - -bool Array::isValid(int maxSize) const -{ - if (size > (uint)maxSize || tableOffset + length*sizeof(offset) > size) - return false; - - for (uint i = 0; i < length; ++i) { - if (!at(i).isValid(this)) - return false; - } - return true; -} - - -bool Entry::operator ==(QStringView key) const -{ - if (value.latinKey) - return (shallowLatin1Key() == key); - else - return (shallowKey() == key); -} - -bool Entry::operator==(QLatin1String key) const -{ - if (value.latinKey) - return shallowLatin1Key() == key; - else - return shallowKey() == QString(key); // ### conversion to QString -} - -bool Entry::operator ==(const Entry &other) const -{ - if (value.latinKey) { - if (other.value.latinKey) - return shallowLatin1Key() == other.shallowLatin1Key(); - return shallowLatin1Key() == other.shallowKey(); - } else if (other.value.latinKey) { - return shallowKey() == other.shallowLatin1Key(); - } - return shallowKey() == other.shallowKey(); -} - -bool Entry::operator >=(const Entry &other) const -{ - if (value.latinKey) { - if (other.value.latinKey) - return shallowLatin1Key() >= other.shallowLatin1Key(); - return shallowLatin1Key() >= other.shallowKey(); - } else if (other.value.latinKey) { - return shallowKey() >= other.shallowLatin1Key(); - } - return shallowKey() >= other.shallowKey(); -} - - -int Value::usedStorage(const Base *b) const -{ - int s = 0; - switch (type) { - case QJsonValue::Double: - if (latinOrIntValue) - break; - s = sizeof(double); - break; - case QJsonValue::String: { - char *d = data(b); - if (latinOrIntValue) - s = sizeof(ushort) + qFromLittleEndian(*(ushort *)d); - else - s = sizeof(int) + sizeof(ushort) * qFromLittleEndian(*(int *)d); - break; - } - case QJsonValue::Array: - case QJsonValue::Object: - s = base(b)->size; - break; - case QJsonValue::Null: - case QJsonValue::Bool: - default: - break; - } - return alignedSize(s); -} - -inline bool isValidValueOffset(uint offset, uint tableOffset) -{ - return offset >= sizeof(Base) - && offset + sizeof(uint) <= tableOffset; -} - -bool Value::isValid(const Base *b) const -{ - switch (type) { - case QJsonValue::Null: - case QJsonValue::Bool: - return true; - case QJsonValue::Double: - return latinOrIntValue || isValidValueOffset(value, b->tableOffset); - case QJsonValue::String: - if (!isValidValueOffset(value, b->tableOffset)) - return false; - if (latinOrIntValue) - return asLatin1String(b).isValid(b->tableOffset - value); - return asString(b).isValid(b->tableOffset - value); - case QJsonValue::Array: - return isValidValueOffset(value, b->tableOffset) - && static_cast(base(b))->isValid(b->tableOffset - value); - case QJsonValue::Object: - return isValidValueOffset(value, b->tableOffset) - && static_cast(base(b))->isValid(b->tableOffset - value); - default: - return false; - } -} - -/*! - \internal - */ -int Value::requiredStorage(QJsonValue &v, bool *compressed) -{ - *compressed = false; - switch (v.t) { - case QJsonValue::Double: - if (QJsonPrivate::compressedNumber(v.dbl) != INT_MAX) { - *compressed = true; - return 0; - } - return sizeof(double); - case QJsonValue::String: { - QString s = v.toString(); - *compressed = QJsonPrivate::useCompressed(s); - return QJsonPrivate::qStringSize(s, *compressed); - } - case QJsonValue::Array: - case QJsonValue::Object: - if (v.d && v.d->compactionCounter) { - v.detach(); - v.d->compact(); - v.base = static_cast(v.d->header->root()); - } - return v.base ? uint(v.base->size) : sizeof(QJsonPrivate::Base); - case QJsonValue::Undefined: - case QJsonValue::Null: - case QJsonValue::Bool: - break; - } - return 0; -} - -/*! - \internal - */ -uint Value::valueToStore(const QJsonValue &v, uint offset) -{ - switch (v.t) { - case QJsonValue::Undefined: - case QJsonValue::Null: - break; - case QJsonValue::Bool: - return v.b; - case QJsonValue::Double: { - int c = QJsonPrivate::compressedNumber(v.dbl); - if (c != INT_MAX) - return c; - } - Q_FALLTHROUGH(); - case QJsonValue::String: - case QJsonValue::Array: - case QJsonValue::Object: - return offset; - } - return 0; -} - -/*! - \internal - */ -void Value::copyData(const QJsonValue &v, char *dest, bool compressed) -{ - switch (v.t) { - case QJsonValue::Double: - if (!compressed) { - qToLittleEndian(v.ui, dest); - } - break; - case QJsonValue::String: { - QString str = v.toString(); - QJsonPrivate::copyString(dest, str, compressed); - break; - } - case QJsonValue::Array: - case QJsonValue::Object: { - const QJsonPrivate::Base *b = v.base; - if (!b) - b = (v.t == QJsonValue::Array ? &emptyArray : &emptyObject); - memcpy(dest, b, b->size); - break; - } - default: - break; - } -} - -} // namespace QJsonPrivate - -QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h index 7978bed7da..d2bbf928d0 100644 --- a/src/corelib/serialization/qjson_p.h +++ b/src/corelib/serialization/qjson_p.h @@ -52,743 +52,170 @@ // We mean it. // -#include #include -#include -#include -#include -#include -#include -#include - -#include "private/qendian_p.h" -#include "private/qsimd_p.h" - -#include -#include -#include +#include +#include QT_BEGIN_NAMESPACE -// in qstring.cpp -void qt_to_latin1_unchecked(uchar *dst, const ushort *uc, qsizetype len); -void qt_from_latin1(ushort *dst, const char *str, size_t size) noexcept; - -/* - This defines a binary data structure for Json data. The data structure is optimised for fast reading - and minimum allocations. The whole data structure can be mmap'ed and used directly. - - In most cases the binary structure is not as space efficient as a utf8 encoded text representation, but - much faster to access. - - The size requirements are: - - String: - Latin1 data: 2 bytes header + string.length() - Full Unicode: 4 bytes header + 2*(string.length()) - - Values: 4 bytes + size of data (size can be 0 for some data) - bool: 0 bytes - double: 8 bytes (0 if integer with less than 27bits) - string: see above - array: size of array - object: size of object - Array: 12 bytes + 4*length + size of Value data - Object: 12 bytes + 8*length + size of Key Strings + size of Value data - - For an example such as - - { // object: 12 + 5*8 = 52 - "firstName": "John", // key 12, value 8 = 20 - "lastName" : "Smith", // key 12, value 8 = 20 - "age" : 25, // key 8, value 0 = 8 - "address" : // key 12, object below = 140 - { // object: 12 + 4*8 - "streetAddress": "21 2nd Street", // key 16, value 16 - "city" : "New York", // key 8, value 12 - "state" : "NY", // key 8, value 4 - "postalCode" : "10021" // key 12, value 8 - }, // object total: 128 - "phoneNumber": // key: 16, value array below = 172 - [ // array: 12 + 2*4 + values below: 156 - { // object 12 + 2*8 - "type" : "home", // key 8, value 8 - "number": "212 555-1234" // key 8, value 16 - }, // object total: 68 - { // object 12 + 2*8 - "type" : "fax", // key 8, value 8 - "number": "646 555-4567" // key 8, value 16 - } // object total: 68 - ] // array total: 156 - } // great total: 412 bytes - - The uncompressed text file used roughly 500 bytes, so in this case we end up using about - the same space as the text representation. - - Other measurements have shown a slightly bigger binary size than a compact text - representation where all possible whitespace was stripped out. -*/ -#define Q_DECLARE_JSONPRIVATE_TYPEINFO(Class, Flags) } Q_DECLARE_TYPEINFO(QJsonPrivate::Class, Flags); namespace QJsonPrivate { namespace QJsonPrivate { -class Array; -class Object; -class Value; -class Entry; - -template -using q_littleendian = QLEInteger; - -typedef q_littleendian qle_short; -typedef q_littleendian qle_ushort; -typedef q_littleendian qle_int; -typedef q_littleendian qle_uint; - -template -using qle_bitfield = QLEIntegerBitfield; - -template -using qle_signedbitfield = QLEIntegerBitfield; - -typedef qle_uint offset; - -// round the size up to the next 4 byte boundary -inline int alignedSize(int size) { return (size + 3) & ~3; } - -const int MaxLatin1Length = 0x7fff; - -static inline bool useCompressed(QStringView s) -{ - if (s.length() > MaxLatin1Length) - return false; - return QtPrivate::isLatin1(s); -} - -static inline bool useCompressed(QLatin1String s) -{ - return s.size() <= MaxLatin1Length; -} - -template -static inline int qStringSize(T string, bool compress) -{ - int l = 2 + string.size(); - if (!compress) - l *= 2; - return alignedSize(l); -} - -// returns INT_MAX if it can't compress it into 28 bits -static inline int compressedNumber(double d) -{ - // this relies on details of how ieee floats are represented - const int exponent_off = 52; - const quint64 fraction_mask = 0x000fffffffffffffull; - const quint64 exponent_mask = 0x7ff0000000000000ull; - - quint64 val; - memcpy (&val, &d, sizeof(double)); - int exp = (int)((val & exponent_mask) >> exponent_off) - 1023; - if (exp < 0 || exp > 25) - return INT_MAX; - - quint64 non_int = val & (fraction_mask >> exp); - if (non_int) - return INT_MAX; - - bool neg = (val >> 63) != 0; - val &= fraction_mask; - val |= ((quint64)1 << 52); - int res = (int)(val >> (52 - exp)); - return neg ? -res : res; -} - -class Latin1String; - -class String -{ -public: - explicit String(const char *data) { d = (Data *)data; } - - struct Data { - qle_uint length; - qle_ushort utf16[1]; - }; - - Data *d; - - int byteSize() const { return sizeof(uint) + sizeof(ushort) * d->length; } - bool isValid(int maxSize) const { - // Check byteSize() <= maxSize, avoiding integer overflow - maxSize -= sizeof(uint); - return maxSize >= 0 && uint(d->length) <= maxSize / sizeof(ushort); - } - - inline String &operator=(QStringView str) - { - d->length = str.length(); - qToLittleEndian(str.utf16(), str.length(), d->utf16); - fillTrailingZeros(); - return *this; - } - - inline String &operator=(QLatin1String str) - { - d->length = str.size(); -#if Q_BYTE_ORDER == Q_BIG_ENDIAN - for (int i = 0; i < str.size(); ++i) - d->utf16[i] = str[i].unicode(); -#else - qt_from_latin1((ushort *)d->utf16, str.data(), str.size()); -#endif - fillTrailingZeros(); - return *this; - } - - void fillTrailingZeros() - { - if (d->length & 1) - d->utf16[d->length] = 0; - } - - inline bool operator ==(QStringView str) const { - int slen = str.length(); - int l = d->length; - if (slen != l) - return false; - const ushort *s = (const ushort *)str.utf16(); - const qle_ushort *a = d->utf16; - const ushort *b = s; - while (l-- && *a == *b) - a++,b++; - return (l == -1); - } - inline bool operator !=(QStringView str) const { - return !operator ==(str); - } - inline bool operator >=(QStringView str) const { - // ### - return toString() >= str; - } - - inline bool operator<(const Latin1String &str) const; - inline bool operator>=(const Latin1String &str) const { return !operator <(str); } - inline bool operator ==(const Latin1String &str) const; - - inline bool operator ==(const String &str) const { - if (d->length != str.d->length) - return false; - return !memcmp(d->utf16, str.d->utf16, d->length*sizeof(ushort)); - } - inline bool operator<(const String &other) const; - inline bool operator >=(const String &other) const { return !(*this < other); } - - inline QString toString() const { -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - return QString((QChar *)d->utf16, d->length); -#else - int l = d->length; - QString str(l, Qt::Uninitialized); - QChar *ch = str.data(); - for (int i = 0; i < l; ++i) - ch[i] = QChar(d->utf16[i]); - return str; -#endif - } - -}; - -class Latin1String +template +struct ObjectIterator { -public: - explicit Latin1String(const char *data) { d = (Data *)data; } - - struct Data { - qle_ushort length; - char latin1[1]; - }; - Data *d; - - int byteSize() const { return sizeof(ushort) + sizeof(char)*(d->length); } - bool isValid(int maxSize) const { - return byteSize() <= maxSize; - } - - inline Latin1String &operator=(QStringView str) - { - int len = d->length = str.length(); - uchar *l = (uchar *)d->latin1; - const ushort *uc = (const ushort *)str.utf16(); - qt_to_latin1_unchecked(l, uc, len); + using pointer = Element *; - fillTrailingZeros(); - return *this; - } + struct value_type; + struct reference { + reference(Element &ref) : m_key(&ref) {} - inline Latin1String &operator=(QLatin1String str) - { - int len = d->length = str.size(); - uchar *l = (uchar *)d->latin1; - memcpy(l, str.data(), len); + reference() = delete; + ~reference() = default; - fillTrailingZeros(); - return *this; - } - - void fillTrailingZeros() - { - uchar *l = (uchar *)d->latin1; - for (int len = d->length; (quintptr)(l + len) & 0x3; ++len) - l[len] = 0; - } - - QLatin1String toQLatin1String() const noexcept { - return QLatin1String(d->latin1, d->length); - } + reference(const reference &other) = default; + reference(reference &&other) = default; - inline bool operator<(const String &str) const - { - const qle_ushort *uc = (qle_ushort *) str.d->utf16; - if (!uc || *uc == 0) - return false; - - const uchar *c = (uchar *)d->latin1; - const uchar *e = c + qMin((int)d->length, (int)str.d->length); - - while (c < e) { - if (*c != *uc) - break; - ++c; - ++uc; + reference &operator=(const value_type &value); + reference &operator=(const reference &other) + { + if (m_key != other.m_key) { + key() = other.key(); + value() = other.value(); + } + return *this; } - return (c == e ? (int)d->length < (int)str.d->length : *c < *uc); - - } - inline bool operator ==(const String &str) const { - return (str == *this); - } - inline bool operator >=(const String &str) const { - return !(*this < str); - } - - inline QString toString() const { - return QString::fromLatin1(d->latin1, d->length); - } -}; - -#define DEF_OP(op) \ - inline bool operator op(Latin1String lhs, Latin1String rhs) noexcept \ - { \ - return lhs.toQLatin1String() op rhs.toQLatin1String(); \ - } \ - inline bool operator op(QLatin1String lhs, Latin1String rhs) noexcept \ - { \ - return lhs op rhs.toQLatin1String(); \ - } \ - inline bool operator op(Latin1String lhs, QLatin1String rhs) noexcept \ - { \ - return lhs.toQLatin1String() op rhs; \ - } \ - inline bool operator op(QStringView lhs, Latin1String rhs) noexcept \ - { \ - return lhs op rhs.toQLatin1String(); \ - } \ - inline bool operator op(Latin1String lhs, QStringView rhs) noexcept \ - { \ - return lhs.toQLatin1String() op rhs; \ - } \ - /*end*/ -DEF_OP(==) -DEF_OP(!=) -DEF_OP(< ) -DEF_OP(> ) -DEF_OP(<=) -DEF_OP(>=) -#undef DEF_OP - -inline bool String::operator ==(const Latin1String &str) const -{ - if ((int)d->length != (int)str.d->length) - return false; - const qle_ushort *uc = d->utf16; - const qle_ushort *e = uc + d->length; - const uchar *c = (uchar *)str.d->latin1; - - while (uc < e) { - if (*uc != *c) - return false; - ++uc; - ++c; - } - return true; -} - -inline bool String::operator <(const String &other) const -{ - int alen = d->length; - int blen = other.d->length; - int l = qMin(alen, blen); - qle_ushort *a = d->utf16; - qle_ushort *b = other.d->utf16; - - while (l-- && *a == *b) - a++,b++; - if (l==-1) - return (alen < blen); - return (ushort)*a < (ushort)*b; -} - -inline bool String::operator<(const Latin1String &str) const -{ - const uchar *c = (uchar *) str.d->latin1; - if (!c || *c == 0) - return false; - - const qle_ushort *uc = d->utf16; - const qle_ushort *e = uc + qMin((int)d->length, (int)str.d->length); - - while (uc < e) { - if (*uc != *c) - break; - ++uc; - ++c; - } - return (uc == e ? (int)d->length < (int)str.d->length : (ushort)*uc < *c); - -} -template -static inline void copyString(char *dest, T str, bool compress) -{ - if (compress) { - Latin1String string(dest); - string = str; - } else { - String string(dest); - string = str; - } -} + reference &operator=(reference &&other) + { + key() = other.key(); + value() = other.value(); + return *this; + } + Element &key() { return *m_key; } + Element &value() { return *(m_key + 1); } -/* - Base is the base class for both Object and Array. Both classes work more or less the same way. - The class starts with a header (defined by the struct below), then followed by data (the data for - values in the Array case and Entry's (see below) for objects. + const Element &key() const { return *m_key; } + const Element &value() const { return *(m_key + 1); } - After the data a table follows (tableOffset points to it) containing Value objects for Arrays, and - offsets from the beginning of the object to Entry's in the case of Object. - Entry's in the Object's table are lexicographically sorted by key in the table(). This allows the usage - of a binary search over the keys in an Object. - */ -class Base -{ -public: - qle_uint size; - union { - uint _dummy; - qle_bitfield<0, 1> is_object; - qle_bitfield<1, 31> length; + private: + Element *m_key; }; - offset tableOffset; - // content follows here - inline bool isObject() const { return !!is_object; } - inline bool isArray() const { return !isObject(); } + struct value_type { + value_type(reference ref) : m_key(ref.key()), m_value(ref.value()) {} - inline offset *table() const { return (offset *) (((char *) this) + tableOffset); } - - int reserveSpace(uint dataSize, int posInTable, uint numItems, bool replace); - void removeItems(int pos, int numItems); -}; - -class Object : public Base -{ -public: - Entry *entryAt(int i) const { - return reinterpret_cast(((char *)this) + table()[i]); - } - int indexOf(QStringView key, bool *exists) const; - int indexOf(QLatin1String key, bool *exists) const; + Element key() const { return m_key; } + Element value() const { return m_value; } + private: + Element m_key; + Element m_value; + }; - bool isValid(int maxSize) const; -}; + using difference_type = typename QVector::difference_type; + using iterator_category = std::random_access_iterator_tag; + ObjectIterator() = default; + ObjectIterator(ElementsIterator it) : it(it) {} + ElementsIterator elementsIterator() { return it; } -class Array : public Base -{ -public: - inline Value at(int i) const; - inline Value &operator [](int i); + ObjectIterator operator++(int) { ObjectIterator ret(it); it += 2; return ret; } + ObjectIterator &operator++() { it += 2; return *this; } + ObjectIterator &operator+=(difference_type n) { it += 2 * n; return *this; } - bool isValid(int maxSize) const; -}; + ObjectIterator operator--(int) { ObjectIterator ret(it); it -= 2; return ret; } + ObjectIterator &operator--() { it -= 2; return *this; } + ObjectIterator &operator-=(difference_type n) { it -= 2 * n; return *this; } + reference operator*() const { return *it; } + reference operator[](int n) const { return it[n * 2]; } -class Value -{ -public: - enum { - MaxSize = (1<<27) - 1 - }; - union { - uint _dummy; - qle_bitfield<0, 3> type; - qle_bitfield<3, 1> latinOrIntValue; - qle_bitfield<4, 1> latinKey; - qle_bitfield<5, 27> value; - qle_signedbitfield<5, 27> int_value; - }; + bool operator<(ObjectIterator other) const { return it < other.it; } + bool operator>(ObjectIterator other) const { return it > other.it; } + bool operator<=(ObjectIterator other) const { return it <= other.it; } + bool operator>=(ObjectIterator other) const { return it >= other.it; } - inline char *data(const Base *b) const { return ((char *)b) + value; } - int usedStorage(const Base *b) const; - - bool toBoolean() const; - double toDouble(const Base *b) const; - QString toString(const Base *b) const; - String asString(const Base *b) const; - Latin1String asLatin1String(const Base *b) const; - Base *base(const Base *b) const; - - bool isValid(const Base *b) const; - - static int requiredStorage(QJsonValue &v, bool *compressed); - static uint valueToStore(const QJsonValue &v, uint offset); - static void copyData(const QJsonValue &v, char *dest, bool compressed); +private: + ElementsIterator it; }; -Q_DECLARE_JSONPRIVATE_TYPEINFO(Value, Q_PRIMITIVE_TYPE) -inline Value Array::at(int i) const +template +inline ObjectIterator operator+( + ObjectIterator a, + typename ObjectIterator::difference_type n) { - return *(Value *) (table() + i); + return {a.elementsIterator() + 2 * n}; } - -inline Value &Array::operator [](int i) +template +inline ObjectIterator operator+( + int n, ObjectIterator a) { - return *(Value *) (table() + i); + return {a.elementsIterator() + 2 * n}; } - - - -class Entry { -public: - Value value; - // key - // value data follows key - - uint size() const { - int s = sizeof(Entry); - if (value.latinKey) - s += shallowLatin1Key().byteSize(); - else - s += shallowKey().byteSize(); - return alignedSize(s); - } - - int usedStorage(Base *b) const { - return size() + value.usedStorage(b); - } - - String shallowKey() const - { - Q_ASSERT(!value.latinKey); - return String((const char *)this + sizeof(Entry)); - } - Latin1String shallowLatin1Key() const - { - Q_ASSERT(value.latinKey); - return Latin1String((const char *)this + sizeof(Entry)); - } - QString key() const - { - if (value.latinKey) { - return shallowLatin1Key().toString(); - } - return shallowKey().toString(); - } - - bool isValid(int maxSize) const { - if (maxSize < (int)sizeof(Entry)) - return false; - maxSize -= sizeof(Entry); - if (value.latinKey) - return shallowLatin1Key().isValid(maxSize); - return shallowKey().isValid(maxSize); - } - - bool operator ==(QStringView key) const; - inline bool operator !=(QStringView key) const { return !operator ==(key); } - inline bool operator >=(QStringView key) const; - - bool operator==(QLatin1String key) const; - inline bool operator!=(QLatin1String key) const { return !operator ==(key); } - inline bool operator>=(QLatin1String key) const; - - bool operator ==(const Entry &other) const; - bool operator >=(const Entry &other) const; -}; - -inline bool Entry::operator >=(QStringView key) const +template +inline ObjectIterator operator-( + ObjectIterator a, + typename ObjectIterator::difference_type n) { - if (value.latinKey) - return (shallowLatin1Key() >= key); - else - return (shallowKey() >= key); + return {a.elementsIterator() - 2 * n}; } - -inline bool Entry::operator >=(QLatin1String key) const +template +inline int operator-( + ObjectIterator a, + ObjectIterator b) { - if (value.latinKey) - return shallowLatin1Key() >= key; - else - return shallowKey() >= QString(key); // ### conversion to QString + return (a.elementsIterator() - b.elementsIterator()) / 2; } - -inline bool operator <(QStringView key, const Entry &e) -{ return e >= key; } - -inline bool operator<(QLatin1String key, const Entry &e) -{ return e >= key; } - - -class Header { -public: - qle_uint tag; // 'qbjs' - qle_uint version; // 1 - Base *root() { return (Base *)(this + 1); } -}; - - -inline bool Value::toBoolean() const +template +inline bool operator!=( + ObjectIterator a, + ObjectIterator b) { - Q_ASSERT(type == QJsonValue::Bool); - return value != 0; + return a.elementsIterator() != b.elementsIterator(); } - -inline double Value::toDouble(const Base *b) const +template +inline bool operator==( + ObjectIterator a, + ObjectIterator b) { - Q_ASSERT(type == QJsonValue::Double); - if (latinOrIntValue) - return int_value; - - quint64 i = qFromLittleEndian((const uchar *)b + value); - double d; - memcpy(&d, &i, sizeof(double)); - return d; + return a.elementsIterator() == b.elementsIterator(); } -inline String Value::asString(const Base *b) const -{ - Q_ASSERT(type == QJsonValue::String && !latinOrIntValue); - return String(data(b)); -} +using KeyIterator = ObjectIterator::iterator>; +using ConstKeyIterator = ObjectIterator::const_iterator>; -inline Latin1String Value::asLatin1String(const Base *b) const +template<> +inline KeyIterator::reference &KeyIterator::reference::operator=(const KeyIterator::value_type &value) { - Q_ASSERT(type == QJsonValue::String && latinOrIntValue); - return Latin1String(data(b)); + *m_key = value.key(); + *(m_key + 1) = value.value(); + return *this; } -inline QString Value::toString(const Base *b) const +inline void swap(KeyIterator::reference a, KeyIterator::reference b) { - if (latinOrIntValue) - return asLatin1String(b).toString(); - else - return asString(b).toString(); + KeyIterator::value_type t = a; + a = b; + b = t; } -inline Base *Value::base(const Base *b) const +class Value { - Q_ASSERT(type == QJsonValue::Array || type == QJsonValue::Object); - return reinterpret_cast(data(b)); -} - -class Data { public: - enum Validation { - Unchecked, - Validated, - Invalid - }; - - QAtomicInt ref; - int alloc; - union { - char *rawData; - Header *header; - }; - uint compactionCounter : 31; - uint ownsData : 1; + static QCborContainerPrivate *container(const QCborValue &v) { return v.container; } - inline Data(char *raw, int a) - : alloc(a), rawData(raw), compactionCounter(0), ownsData(true) + static QJsonValue fromTrustedCbor(const QCborValue &v) { + QJsonValue result; + result.d = v.container; + result.n = v.n; + result.t = v.t; + return result; } - inline Data(int reserved, QJsonValue::Type valueType) - : rawData(nullptr), compactionCounter(0), ownsData(true) - { - Q_ASSERT(valueType == QJsonValue::Array || valueType == QJsonValue::Object); - - alloc = sizeof(Header) + sizeof(Base) + reserved + sizeof(offset); - header = (Header *)malloc(alloc); - Q_CHECK_PTR(header); - header->tag = QJsonDocument::BinaryFormatTag; - header->version = 1; - Base *b = header->root(); - b->size = sizeof(Base); - b->is_object = (valueType == QJsonValue::Object); - b->tableOffset = sizeof(Base); - b->length = 0; - } - inline ~Data() - { if (ownsData) free(rawData); } - - uint offsetOf(const void *ptr) const { return (uint)(((char *)ptr - rawData)); } - - QJsonObject toObject(Object *o) const - { - return QJsonObject(const_cast(this), o); - } - - QJsonArray toArray(Array *a) const - { - return QJsonArray(const_cast(this), a); - } - - Data *clone(Base *b, int reserve = 0) - { - int size = sizeof(Header) + b->size; - if (b == header->root() && ref.loadRelaxed() == 1 && alloc >= size + reserve) - return this; - - if (reserve) { - if (reserve < 128) - reserve = 128; - size = qMax(size + reserve, qMin(size *2, (int)Value::MaxSize)); - if (size > Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure"); - return nullptr; - } - } - char *raw = (char *)malloc(size); - Q_CHECK_PTR(raw); - memcpy(raw + sizeof(Header), b, b->size); - Header *h = (Header *)raw; - h->tag = QJsonDocument::BinaryFormatTag; - h->version = 1; - Data *d = new Data(raw, size); - d->compactionCounter = (b == header->root()) ? compactionCounter : 0; - return d; - } - - void compact(); - bool valid() const; - -private: - Q_DISABLE_COPY_MOVE(Data) }; -} +} // namespace QJsonPrivate QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp index 9636ac5856..c5d4bf0315 100644 --- a/src/corelib/serialization/qjsonarray.cpp +++ b/src/corelib/serialization/qjsonarray.cpp @@ -40,12 +40,16 @@ #include #include #include +#include #include +#include #include #include +#include +#include + #include "qjsonwriter_p.h" -#include "qjson_p.h" QT_BEGIN_NAMESPACE @@ -131,10 +135,7 @@ QT_BEGIN_NAMESPACE /*! Creates an empty array. */ -QJsonArray::QJsonArray() - : d(nullptr), a(nullptr) -{ -} +QJsonArray::QJsonArray() = default; /*! \fn QJsonArray::QJsonArray(std::initializer_list args) @@ -151,12 +152,10 @@ QJsonArray::QJsonArray() /*! \internal */ -QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) - : d(data), a(array) +QJsonArray::QJsonArray(QCborContainerPrivate *array) + : a(array) { - Q_ASSERT(data); Q_ASSERT(array); - d->ref.ref(); } /*! @@ -168,18 +167,13 @@ QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array) */ void QJsonArray::initialize() { - d = nullptr; a = nullptr; } /*! Deletes the array. */ -QJsonArray::~QJsonArray() -{ - if (d && !d->ref.deref()) - delete d; -} +QJsonArray::~QJsonArray() = default; /*! Creates a copy of \a other. @@ -187,12 +181,22 @@ QJsonArray::~QJsonArray() Since QJsonArray is implicitly shared, the copy is shallow as long as the object doesn't get modified. */ +QJsonArray::QJsonArray(std::initializer_list args) +{ + initialize(); + for (const auto & arg : args) + append(arg); +} + QJsonArray::QJsonArray(const QJsonArray &other) { - d = other.d; a = other.a; - if (d) - d->ref.ref(); +} + +QJsonArray::QJsonArray(QJsonArray &&other) noexcept + : a(other.a) +{ + other.a = nullptr; } /*! @@ -200,15 +204,7 @@ QJsonArray::QJsonArray(const QJsonArray &other) */ QJsonArray &QJsonArray::operator =(const QJsonArray &other) { - if (d != other.d) { - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); - } a = other.a; - return *this; } @@ -282,48 +278,7 @@ QJsonArray QJsonArray::fromStringList(const QStringList &list) */ QJsonArray QJsonArray::fromVariantList(const QVariantList &list) { - QJsonArray array; - if (list.isEmpty()) - return array; - - array.detach2(1024); - - QVector values; - values.resize(list.size()); - QJsonPrivate::Value *valueData = values.data(); - uint currentOffset = sizeof(QJsonPrivate::Base); - - for (int i = 0; i < list.size(); ++i) { - QJsonValue val = QJsonValue::fromVariant(list.at(i)); - - bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); - - if (!array.detach2(valueSize)) - return QJsonArray(); - - QJsonPrivate::Value *v = valueData + i; - v->type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); - v->latinOrIntValue = latinOrIntValue; - v->latinKey = false; - v->value = QJsonPrivate::Value::valueToStore(val, currentOffset); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)array.a + currentOffset, latinOrIntValue); - - currentOffset += valueSize; - array.a->size = currentOffset; - } - - // write table - array.a->tableOffset = currentOffset; - if (!array.detach2(sizeof(QJsonPrivate::offset)*values.size())) - return QJsonArray(); - memcpy(static_cast(array.a->table()), - static_cast(values.constData()), values.size()*sizeof(uint)); - array.a->length = values.size(); - array.a->size = currentOffset + sizeof(QJsonPrivate::offset)*values.size(); - - return array; + return QCborArray::fromVariantList(list).toJsonArray(); } /*! @@ -333,14 +288,7 @@ QJsonArray QJsonArray::fromVariantList(const QVariantList &list) */ QVariantList QJsonArray::toVariantList() const { - QVariantList list; - - if (a) { - list.reserve(a->length); - for (int i = 0; i < (int)a->length; ++i) - list.append(QJsonValue(d, a, a->at(i)).toVariant()); - } - return list; + return QCborArray::fromJsonArray(*this).toVariantList(); } @@ -349,10 +297,7 @@ QVariantList QJsonArray::toVariantList() const */ int QJsonArray::size() const { - if (!d) - return 0; - - return (int)a->length; + return a ? a->elements.size() : 0; } /*! @@ -370,10 +315,7 @@ int QJsonArray::size() const */ bool QJsonArray::isEmpty() const { - if (!d) - return true; - - return !a->length; + return a == nullptr || a->elements.isEmpty(); } /*! @@ -384,10 +326,10 @@ bool QJsonArray::isEmpty() const */ QJsonValue QJsonArray::at(int i) const { - if (!a || i < 0 || i >= (int)a->length) + if (!a || i < 0 || i >= a->elements.size()) return QJsonValue(QJsonValue::Undefined); - return QJsonValue(d, a, a->at(i)); + return QJsonPrivate::Value::fromTrustedCbor(a->valueAt(i)); } /*! @@ -411,7 +353,7 @@ QJsonValue QJsonArray::first() const */ QJsonValue QJsonArray::last() const { - return at(a ? (a->length - 1) : 0); + return at(a ? (a->elements.size() - 1) : 0); } /*! @@ -433,7 +375,7 @@ void QJsonArray::prepend(const QJsonValue &value) */ void QJsonArray::append(const QJsonValue &value) { - insert(a ? (int)a->length : 0, value); + insert(a ? a->elements.size() : 0, value); } /*! @@ -444,14 +386,10 @@ void QJsonArray::append(const QJsonValue &value) */ void QJsonArray::removeAt(int i) { - if (!a || i < 0 || i >= (int)a->length) + if (!a || i < 0 || i >= a->elements.length()) return; - detach2(); - a->removeItems(i, 1); - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) - compact(); + a->removeAt(i); } /*! \fn void QJsonArray::removeFirst() @@ -484,11 +422,12 @@ void QJsonArray::removeAt(int i) */ QJsonValue QJsonArray::takeAt(int i) { - if (!a || i < 0 || i >= (int)a->length) + if (!a || i < 0 || i >= a->elements.length()) return QJsonValue(QJsonValue::Undefined); - QJsonValue v(d, a, a->at(i)); - removeAt(i); // detaches + detach2(); + const QJsonValue v = QJsonPrivate::Value::fromTrustedCbor(a->extractAt(i)); + a->removeAt(i); return v; } @@ -501,29 +440,14 @@ QJsonValue QJsonArray::takeAt(int i) */ void QJsonArray::insert(int i, const QJsonValue &value) { - Q_ASSERT (i >= 0 && i <= (a ? (int)a->length : 0)); - QJsonValue val = value; - - bool compressed; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); - - if (!detach2(valueSize + sizeof(QJsonPrivate::Value))) - return; - - if (!a->length) - a->tableOffset = sizeof(QJsonPrivate::Array); - - int valueOffset = a->reserveSpace(valueSize, i, 1, false); - if (!valueOffset) - return; - - QJsonPrivate::Value &v = (*a)[i]; - v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); - v.latinOrIntValue = compressed; - v.latinKey = false; - v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); + if (a) + detach2(a->elements.length() + 1); + else + a = new QCborContainerPrivate; + + Q_ASSERT (i >= 0 && i <= a->elements.length()); + a->insertAt(i, value.type() == QJsonValue::Undefined ? QCborValue(nullptr) + : QCborValue::fromJsonValue(value)); } /*! @@ -552,33 +476,9 @@ void QJsonArray::insert(int i, const QJsonValue &value) */ void QJsonArray::replace(int i, const QJsonValue &value) { - Q_ASSERT (a && i >= 0 && i < (int)(a->length)); - QJsonValue val = value; - - bool compressed; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &compressed); - - if (!detach2(valueSize)) - return; - - if (!a->length) - a->tableOffset = sizeof(QJsonPrivate::Array); - - int valueOffset = a->reserveSpace(valueSize, i, 1, true); - if (!valueOffset) - return; - - QJsonPrivate::Value &v = (*a)[i]; - v.type = (val.t == QJsonValue::Undefined ? QJsonValue::Null : val.t); - v.latinOrIntValue = compressed; - v.latinKey = false; - v.value = QJsonPrivate::Value::valueToStore(val, valueOffset); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)a + valueOffset, compressed); - - ++d->compactionCounter; - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(a->length) / 2u) - compact(); + Q_ASSERT (a && i >= 0 && i < a->elements.length()); + detach2(); + a->replaceAt(i, QCborValue::fromJsonValue(value)); } /*! @@ -610,7 +510,7 @@ bool QJsonArray::contains(const QJsonValue &value) const */ QJsonValueRef QJsonArray::operator [](int i) { - Q_ASSERT(a && i >= 0 && i < (int)a->length); + Q_ASSERT(a && i >= 0 && i < a->elements.length()); return QJsonValueRef(this, i); } @@ -633,14 +533,14 @@ bool QJsonArray::operator==(const QJsonArray &other) const return true; if (!a) - return !other.a->length; + return !other.a->elements.length(); if (!other.a) - return !a->length; - if (a->length != other.a->length) + return !a->elements.length(); + if (a->elements.length() != other.a->elements.length()) return false; - for (int i = 0; i < (int)a->length; ++i) { - if (QJsonValue(d, a, a->at(i)) != QJsonValue(other.d, other.a, other.a->at(i))) + for (int i = 0; i < a->elements.length(); ++i) { + if (a->valueAt(i) != other.a->valueAt(i)) return false; } return true; @@ -1216,28 +1116,10 @@ void QJsonArray::detach(uint reserve) */ bool QJsonArray::detach2(uint reserve) { - if (!d) { - if (reserve >= QJsonPrivate::Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure"); - return false; - } - d = new QJsonPrivate::Data(reserve, QJsonValue::Array); - a = static_cast(d->header->root()); - d->ref.ref(); - return true; - } - if (reserve == 0 && d->ref.loadRelaxed() == 1) + if (!a) return true; - - QJsonPrivate::Data *x = d->clone(a, reserve); - if (!x) - return false; - x->ref.ref(); - if (!d->ref.deref()) - delete d; - d = x; - a = static_cast(d->header->root()); - return true; + a = a->detach(a.data(), reserve ? reserve : size()); + return a; } /*! @@ -1245,12 +1127,7 @@ bool QJsonArray::detach2(uint reserve) */ void QJsonArray::compact() { - if (!d || !d->compactionCounter) - return; - - detach2(); - d->compact(); - a = static_cast(d->header->root()); + a->compact(a->elements.size()); } uint qHash(const QJsonArray &array, uint seed) @@ -1267,7 +1144,7 @@ QDebug operator<<(QDebug dbg, const QJsonArray &a) return dbg; } QByteArray json; - QJsonPrivate::Writer::arrayToJson(a.a, json, 0, true); + QJsonPrivate::Writer::arrayToJson(a.a.data(), json, 0, true); dbg.nospace() << "QJsonArray(" << json.constData() // print as utf-8 string without extra quotation marks << ")"; diff --git a/src/corelib/serialization/qjsonarray.h b/src/corelib/serialization/qjsonarray.h index 983a6753b5..22aa996a3e 100644 --- a/src/corelib/serialization/qjsonarray.h +++ b/src/corelib/serialization/qjsonarray.h @@ -42,6 +42,7 @@ #include #include +#include #include QT_BEGIN_NAMESPACE @@ -56,25 +57,14 @@ class Q_CORE_EXPORT QJsonArray public: QJsonArray(); - QJsonArray(std::initializer_list args) - { - initialize(); - for (std::initializer_list::const_iterator i = args.begin(); i != args.end(); ++i) - append(*i); - } + QJsonArray(std::initializer_list args); ~QJsonArray(); QJsonArray(const QJsonArray &other); QJsonArray &operator =(const QJsonArray &other); - QJsonArray(QJsonArray &&other) noexcept - : d(other.d), - a(other.a) - { - other.d = nullptr; - other.a = nullptr; - } + QJsonArray(QJsonArray &&other) noexcept; QJsonArray &operator =(QJsonArray &&other) noexcept { @@ -113,7 +103,6 @@ public: void swap(QJsonArray &other) noexcept { - qSwap(d, other.d); qSwap(a, other.a); } @@ -245,20 +234,21 @@ public: typedef int difference_type; private: - friend class QJsonPrivate::Data; friend class QJsonValue; friend class QJsonDocument; + friend class QCborArray; friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); - QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array); + QJsonArray(QCborContainerPrivate *array); void initialize(); void compact(); // ### Qt 6: remove me and merge with detach2 void detach(uint reserve = 0); bool detach2(uint reserve = 0); - QJsonPrivate::Data *d; - QJsonPrivate::Array *a; + // ### Qt 6: remove + void *dead = nullptr; + QExplicitlySharedDataPointer a; }; Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonArray) diff --git a/src/corelib/serialization/qjsoncbor.cpp b/src/corelib/serialization/qjsoncbor.cpp index d6a0c8c48a..7136a163ee 100644 --- a/src/corelib/serialization/qjsoncbor.cpp +++ b/src/corelib/serialization/qjsoncbor.cpp @@ -42,6 +42,10 @@ #include "qcborarray.h" #include "qcbormap.h" + +#include "qjsonarray.h" +#include "qjsonobject.h" +#include "qjsondocument.h" #include "qjson_p.h" #include @@ -149,7 +153,12 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety case QCborValue::Array: case QCborValue::Map: +#if defined(QT_JSON_READONLY) || defined(QT_BOOTSTRAPPED) + qFatal("Writing JSON is disabled."); + return QString(); +#else return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact); +#endif case QCborValue::SimpleType: break; @@ -181,30 +190,11 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety return simpleTypeString(e.type); } -static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx); - -static QJsonArray convertToJsonArray(const QCborContainerPrivate *d) -{ - QJsonArray a; - if (d) { - for (qsizetype idx = 0; idx < d->elements.size(); ++idx) - a.append(convertToJson(d, idx)); - } - return a; -} - -static QJsonObject convertToJsonObject(const QCborContainerPrivate *d) -{ - QJsonObject o; - if (d) { - for (qsizetype idx = 0; idx < d->elements.size(); idx += 2) - o.insert(makeString(d, idx), convertToJson(d, idx + 1)); - } - return o; -} +QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx); -static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d) +static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d) { +#ifndef QT_BUILD_QMAKE qint64 tag = d->elements.at(0).value; switch (tag) { @@ -225,12 +215,36 @@ static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d) return s; } } +#endif // for all other tags, ignore it and return the converted tagged item - return convertToJson(d, 1); + return qt_convertToJson(d, 1); +} + +// We need to do this because sub-objects may need conversion. +static QJsonArray convertToJsonArray(QCborContainerPrivate *d) +{ + QJsonArray a; + if (d) { + for (qsizetype idx = 0; idx < d->elements.size(); ++idx) + a.append(qt_convertToJson(d, idx)); + } + return a; } -static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) +// We need to do this because the keys need to be sorted and converted to strings +// and sub-objects may need recursive conversion. +static QJsonObject convertToJsonObject(QCborContainerPrivate *d) +{ + QJsonObject o; + if (d) { + for (qsizetype idx = 0; idx < d->elements.size(); idx += 2) + o.insert(makeString(d, idx), qt_convertToJson(d, idx + 1)); + } + return o; +} + +QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx) { // encoding the container itself if (idx == -QCborValue::Array) @@ -248,7 +262,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) const auto &e = d->elements.at(idx); switch (e.type) { case QCborValue::Integer: - return qint64(e.value); + return QJsonPrivate::Value::fromTrustedCbor(e.value); case QCborValue::ByteArray: case QCborValue::String: @@ -264,14 +278,14 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) case QCborValue::RegularExpression: case QCborValue::Uuid: // recurse - return convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type); + return qt_convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type); case QCborValue::Null: return QJsonValue(); case QCborValue::Undefined: case QCborValue::Invalid: - return QJsonValue(QJsonValue::Undefined); + return QJsonValue::Undefined; case QCborValue::False: return false; @@ -283,7 +297,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) return fpToJson(e.fpvalue()); } - return makeString(d, idx); + return QJsonPrivate::Value::fromTrustedCbor(makeString(d, idx)); } /*! @@ -348,22 +362,22 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) QJsonValue QCborValue::toJsonValue() const { if (container) - return convertToJson(container, n < 0 ? -type() : n); + return qt_convertToJson(container, n < 0 ? -type() : n); // simple values switch (type()) { - case Integer: - return n; - - case Null: - return QJsonValue(); - case False: return false; + case Integer: + return QJsonPrivate::Value::fromTrustedCbor(n); + case True: return true; + case Null: + return QJsonValue(); + case Double: return fpToJson(fp_helper()); @@ -372,12 +386,12 @@ QJsonValue QCborValue::toJsonValue() const case Undefined: case Invalid: - return QJsonValue(QJsonValue::Undefined); + return QJsonValue::Undefined; case ByteArray: case String: // empty strings - return QString(); + return QJsonValue::String; case Array: // empty array @@ -392,16 +406,16 @@ QJsonValue QCborValue::toJsonValue() const case Url: case RegularExpression: case Uuid: - Q_UNREACHABLE(); + // Reachable, but invalid in Json return QJsonValue::Undefined; } - return simpleTypeString(type()); + return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type())); } QJsonValue QCborValueRef::toJsonValue() const { - return convertToJson(d, i); + return qt_convertToJson(d, i); } /*! @@ -540,8 +554,10 @@ QVariant QCborValue::toVariant() const case DateTime: return toDateTime(); +#ifndef QT_BOOTSTRAPPED case Url: return toUrl(); +#endif #if QT_CONFIG(regularexpression) case RegularExpression: @@ -597,12 +613,13 @@ QCborValue QCborValue::fromJsonValue(const QJsonValue &v) { switch (v.type()) { case QJsonValue::Bool: - return v.b; + return v.toBool(); case QJsonValue::Double: { qint64 i; - if (convertDoubleTo(v.dbl, &i)) + const double dbl = v.toDouble(); + if (convertDoubleTo(dbl, &i)) return i; - return v.dbl; + return dbl; } case QJsonValue::String: return v.toString(); @@ -710,8 +727,10 @@ QCborValue QCborValue::fromVariant(const QVariant &variant) return variant.toByteArray(); case QVariant::DateTime: return QCborValue(variant.toDateTime()); +#ifndef QT_BOOTSTRAPPED case QVariant::Url: return QCborValue(variant.toUrl()); +#endif case QVariant::Uuid: return QCborValue(variant.toUuid()); case QVariant::List: @@ -824,15 +843,9 @@ QCborArray QCborArray::fromVariantList(const QVariantList &list) */ QCborArray QCborArray::fromJsonArray(const QJsonArray &array) { - QCborArray a; - a.detach(array.size()); - for (const QJsonValue &v : array) { - if (v.isString()) - a.d->append(v.toString()); - else - a.d->append(QCborValue::fromJsonValue(v)); - } - return a; + QCborArray result; + result.d = array.a; + return result; } /*! @@ -944,20 +957,9 @@ QCborMap QCborMap::fromVariantHash(const QVariantHash &hash) */ QCborMap QCborMap::fromJsonObject(const QJsonObject &obj) { - QCborMap m; - m.detach(obj.size()); - QCborContainerPrivate *d = m.d.data(); - - auto it = obj.begin(); - auto end = obj.end(); - for ( ; it != end; ++it) { - d->append(it.key()); - if (it.value().isString()) - d->append(it.value().toString()); - else - d->append(QCborValue::fromJsonValue(it.value())); - } - return m; + QCborMap result; + result.d = obj.o; + return result; } QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp index 8c3818caff..b9b1902f34 100644 --- a/src/corelib/serialization/qjsondocument.cpp +++ b/src/corelib/serialization/qjsondocument.cpp @@ -44,11 +44,22 @@ #include #include #include +#include +#include +#include "qcborvalue_p.h" #include "qjsonwriter_p.h" #include "qjsonparser_p.h" #include "qjson_p.h" #include "qdatastream.h" +#if QT_CONFIG(binaryjson) +#include "qbinaryjson_p.h" +#include "qbinaryjsonobject_p.h" +#include "qbinaryjsonarray_p.h" +#endif + +#include + QT_BEGIN_NAMESPACE /*! \class QJsonDocument @@ -80,6 +91,33 @@ QT_BEGIN_NAMESPACE \sa {JSON Support in Qt}, {JSON Save Game Example} */ + +class QJsonDocumentPrivate +{ + Q_DISABLE_COPY_MOVE(QJsonDocumentPrivate); +public: + QJsonDocumentPrivate() = default; + QJsonDocumentPrivate(QCborValue data) : value(std::move(data)) {} + ~QJsonDocumentPrivate() + { + if (rawData) + free(rawData); + } + + QCborValue value; + char *rawData = nullptr; + uint rawDataSize = 0; + + void clearRawData() + { + if (rawData) { + free(rawData); + rawData = nullptr; + rawDataSize = 0; + } + } +}; + /*! * Constructs an empty and invalid document. */ @@ -109,11 +147,10 @@ QJsonDocument::QJsonDocument(const QJsonArray &array) /*! \internal */ -QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) - : d(data) +QJsonDocument::QJsonDocument(const QCborValue &data) + : d(qt_make_unique(data)) { Q_ASSERT(d); - d->ref.ref(); } /*! @@ -121,20 +158,30 @@ QJsonDocument::QJsonDocument(QJsonPrivate::Data *data) Binary data set with fromRawData is not freed. */ -QJsonDocument::~QJsonDocument() -{ - if (d && !d->ref.deref()) - delete d; -} +QJsonDocument::~QJsonDocument() = default; /*! * Creates a copy of the \a other document. */ QJsonDocument::QJsonDocument(const QJsonDocument &other) { - d = other.d; - if (d) - d->ref.ref(); + if (other.d) { + if (!d) + d = qt_make_unique(); + d->value = other.d->value; + } else { + d.reset(); + } +} + +QJsonDocument::QJsonDocument(QJsonDocument &&other) noexcept + : d(std::move(other.d)) +{ +} + +void QJsonDocument::swap(QJsonDocument &other) noexcept +{ + qSwap(d, other.d); } /*! @@ -143,14 +190,17 @@ QJsonDocument::QJsonDocument(const QJsonDocument &other) */ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) { - if (d != other.d) { - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); + if (this != &other) { + if (other.d) { + if (!d) + d = qt_make_unique(); + else + d->clearRawData(); + d->value = other.d->value; + } else { + d.reset(); + } } - return *this; } @@ -187,12 +237,13 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) the application. */ +#if QT_CONFIG(binaryjson) /*! Creates a QJsonDocument that uses the first \a size bytes from \a data. It assumes \a data contains a binary encoded JSON document. - The created document does not take ownership of \a data and the caller - has to guarantee that \a data will not be deleted or modified as long as - any QJsonDocument, QJsonObject or QJsonArray still references the data. + The created document does not take ownership of \a data. The data is + copied into a different data structure, and the original data can be + deleted or modified afterwards. \a data has to be aligned to a 4 byte boundary. @@ -202,7 +253,18 @@ QJsonDocument &QJsonDocument::operator =(const QJsonDocument &other) Returns a QJsonDocument representing the data. - \sa rawData(), fromBinaryData(), isNull(), DataValidation + \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards + compatibility. It is undocumented and restrictive in the maximum size of JSON + documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, + which can in turn be serialized into the CBOR binary format and vice versa. The + CBOR format is a well-defined and less restrictive binary representation for a + superset of JSON. + + \note Before Qt 5.15, the caller had to guarantee that \a data would not be + deleted or modified as long as any QJsonDocument, QJsonObject or QJsonArray + still referenced the data. From Qt 5.15 on, this is not necessary anymore. + + \sa rawData(), fromBinaryData(), isNull(), DataValidation, QCborValue */ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidation validation) { @@ -211,18 +273,15 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat return QJsonDocument(); } - if (size < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) + if (size < 0 || uint(size) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base)) return QJsonDocument(); - QJsonPrivate::Data *d = new QJsonPrivate::Data((char *)data, size); - d->ownsData = false; + std::unique_ptr binaryData + = qt_make_unique(data, size); - if (validation != BypassValidation && !d->valid()) { - delete d; - return QJsonDocument(); - } - - return QJsonDocument(d); + return (validation == BypassValidation || binaryData->isValid()) + ? binaryData->toJsonDocument() + : QJsonDocument(); } /*! @@ -230,7 +289,16 @@ QJsonDocument QJsonDocument::fromRawData(const char *data, int size, DataValidat \a size will contain the size of the returned data. This method is useful to e.g. stream the JSON document - in it's binary form to a file. + in its binary form to a file. + + \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards + compatibility. It is undocumented and restrictive in the maximum size of JSON + documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, + which can in turn be serialized into the CBOR binary format and vice versa. The + CBOR format is a well-defined and less restrictive binary representation for a + superset of JSON. + + \sa QCborValue */ const char *QJsonDocument::rawData(int *size) const { @@ -238,7 +306,21 @@ const char *QJsonDocument::rawData(int *size) const *size = 0; return nullptr; } - *size = d->alloc; + + if (!d->rawData) { + if (isObject()) { + QBinaryJsonObject o = QBinaryJsonObject::fromJsonObject(object()); + d->rawData = o.takeRawData(&(d->rawDataSize)); + } else { + QBinaryJsonArray a = QBinaryJsonArray::fromJsonArray(array()); + d->rawData = a.takeRawData(&(d->rawDataSize)); + } + } + + // It would be quite miraculous if not, as we should have hit the 128MB limit then. + Q_ASSERT(d->rawDataSize <= uint(std::numeric_limits::max())); + + *size = d->rawDataSize; return d->rawData; } @@ -249,38 +331,64 @@ const char *QJsonDocument::rawData(int *size) const By default the data is validated. If the \a data is not valid, the method returns a null document. - \sa toBinaryData(), fromRawData(), isNull(), DataValidation + \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards + compatibility. It is undocumented and restrictive in the maximum size of JSON + documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, + which can in turn be serialized into the CBOR binary format and vice versa. The + CBOR format is a well-defined and less restrictive binary representation for a + superset of JSON. + + \sa toBinaryData(), fromRawData(), isNull(), DataValidation, QCborValue */ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidation validation) { - if (data.size() < (int)(sizeof(QJsonPrivate::Header) + sizeof(QJsonPrivate::Base))) + if (uint(data.size()) < sizeof(QBinaryJsonPrivate::Header) + sizeof(QBinaryJsonPrivate::Base)) return QJsonDocument(); - QJsonPrivate::Header h; - memcpy(&h, data.constData(), sizeof(QJsonPrivate::Header)); - QJsonPrivate::Base root; - memcpy(&root, data.constData() + sizeof(QJsonPrivate::Header), sizeof(QJsonPrivate::Base)); + QBinaryJsonPrivate::Header h; + memcpy(&h, data.constData(), sizeof(QBinaryJsonPrivate::Header)); + QBinaryJsonPrivate::Base root; + memcpy(&root, data.constData() + sizeof(QBinaryJsonPrivate::Header), + sizeof(QBinaryJsonPrivate::Base)); - // do basic checks here, so we don't try to allocate more memory than we can. - if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1u || - sizeof(QJsonPrivate::Header) + root.size > (uint)data.size()) + const uint size = sizeof(QBinaryJsonPrivate::Header) + root.size; + if (h.tag != QJsonDocument::BinaryFormatTag || h.version != 1U || size > uint(data.size())) return QJsonDocument(); - const uint size = sizeof(QJsonPrivate::Header) + root.size; - char *raw = (char *)malloc(size); - if (!raw) - return QJsonDocument(); + std::unique_ptr d + = qt_make_unique(data.constData(), size); - memcpy(raw, data.constData(), size); - QJsonPrivate::Data *d = new QJsonPrivate::Data(raw, size); + return (validation == BypassValidation || d->isValid()) + ? d->toJsonDocument() + : QJsonDocument(); +} - if (validation != BypassValidation && !d->valid()) { - delete d; - return QJsonDocument(); - } +/*! + Returns a binary representation of the document. + + The binary representation is also the native format used internally in Qt, + and is very efficient and fast to convert to and from. + + The binary format can be stored on disk and interchanged with other applications + or computers. fromBinaryData() can be used to convert it back into a + JSON document. + + \deprecated in Qt 5.15. The binary JSON encoding is only retained for backwards + compatibility. It is undocumented and restrictive in the maximum size of JSON + documents that can be encoded. Qt JSON types can be converted to Qt CBOR types, + which can in turn be serialized into the CBOR binary format and vice versa. The + CBOR format is a well-defined and less restrictive binary representation for a + superset of JSON. - return QJsonDocument(d); + \sa fromBinaryData(), QCborValue + */ +QByteArray QJsonDocument::toBinaryData() const +{ + int size = 0; + const char *raw = rawData(&size); + return QByteArray(raw, size); } +#endif // QT_CONFIG(binaryjson) /*! Creates a QJsonDocument from the QVariant \a variant. @@ -293,6 +401,7 @@ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidati QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) { QJsonDocument doc; + switch (variant.type()) { case QVariant::Map: doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); @@ -304,7 +413,8 @@ QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) doc.setArray(QJsonArray::fromVariantList(variant.toList())); break; case QVariant::StringList: - doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + doc.d = qt_make_unique(); + doc.d->value = QCborArray::fromStringList(variant.toStringList()); break; default: break; @@ -325,10 +435,10 @@ QVariant QJsonDocument::toVariant() const if (!d) return QVariant(); - if (d->header->root()->isArray()) - return QJsonArray(d, static_cast(d->header->root())).toVariantList(); - else - return QJsonObject(d, static_cast(d->header->root())).toVariantMap(); + QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value); + if (d->value.isArray()) + return QJsonArray(container).toVariantList(); + return QJsonObject(container).toVariantMap(); } /*! @@ -370,10 +480,11 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const if (!d) return json; - if (d->header->root()->isArray()) - QJsonPrivate::Writer::arrayToJson(static_cast(d->header->root()), json, 0, (format == Compact)); + const QCborContainerPrivate *container = QJsonPrivate::Value::container(d->value); + if (d->value.isArray()) + QJsonPrivate::Writer::arrayToJson(container, json, 0, (format == Compact)); else - QJsonPrivate::Writer::objectToJson(static_cast(d->header->root()), json, 0, (format == Compact)); + QJsonPrivate::Writer::objectToJson(container, json, 0, (format == Compact)); return json; } @@ -392,7 +503,13 @@ QByteArray QJsonDocument::toJson(JsonFormat format) const QJsonDocument QJsonDocument::fromJson(const QByteArray &json, QJsonParseError *error) { QJsonPrivate::Parser parser(json.constData(), json.length()); - return parser.parse(error); + QJsonDocument result; + const QCborValue val = parser.parse(error); + if (val.isArray() || val.isMap()) { + result.d = qt_make_unique(); + result.d->value = val; + } + return result; } /*! @@ -406,26 +523,6 @@ bool QJsonDocument::isEmpty() const return false; } -/*! - Returns a binary representation of the document. - - The binary representation is also the native format used internally in Qt, - and is very efficient and fast to convert to and from. - - The binary format can be stored on disk and interchanged with other applications - or computers. fromBinaryData() can be used to convert it back into a - JSON document. - - \sa fromBinaryData() - */ -QByteArray QJsonDocument::toBinaryData() const -{ - if (!d || !d->rawData) - return QByteArray(); - - return QByteArray(d->rawData, d->header->root()->size + sizeof(QJsonPrivate::Header)); -} - /*! Returns \c true if the document contains an array. @@ -436,8 +533,7 @@ bool QJsonDocument::isArray() const if (!d) return false; - QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; - return h->root()->isArray(); + return d->value.isArray(); } /*! @@ -450,8 +546,7 @@ bool QJsonDocument::isObject() const if (!d) return false; - QJsonPrivate::Header *h = (QJsonPrivate::Header *)d->rawData; - return h->root()->isObject(); + return d->value.isMap(); } /*! @@ -464,10 +559,9 @@ bool QJsonDocument::isObject() const */ QJsonObject QJsonDocument::object() const { - if (d) { - QJsonPrivate::Base *b = d->header->root(); - if (b->isObject()) - return QJsonObject(d, static_cast(b)); + if (isObject()) { + if (auto container = QJsonPrivate::Value::container(d->value)) + return QJsonObject(container); } return QJsonObject(); } @@ -482,10 +576,9 @@ QJsonObject QJsonDocument::object() const */ QJsonArray QJsonDocument::array() const { - if (d) { - QJsonPrivate::Base *b = d->header->root(); - if (b->isArray()) - return QJsonArray(d, static_cast(b)); + if (isArray()) { + if (auto container = QJsonPrivate::Value::container(d->value)) + return QJsonArray(container); } return QJsonArray(); } @@ -497,24 +590,12 @@ QJsonArray QJsonDocument::array() const */ void QJsonDocument::setObject(const QJsonObject &object) { - if (d && !d->ref.deref()) - delete d; - - d = object.d; + if (!d) + d = qt_make_unique(); + else + d->clearRawData(); - if (!d) { - d = new QJsonPrivate::Data(0, QJsonValue::Object); - } else if (d->compactionCounter || object.o != d->header->root()) { - QJsonObject o(object); - if (d->compactionCounter) - o.compact(); - else - o.detach2(); - d = o.d; - d->ref.ref(); - return; - } - d->ref.ref(); + d->value = QCborValue::fromJsonValue(object); } /*! @@ -524,24 +605,12 @@ void QJsonDocument::setObject(const QJsonObject &object) */ void QJsonDocument::setArray(const QJsonArray &array) { - if (d && !d->ref.deref()) - delete d; - - d = array.d; + if (!d) + d = qt_make_unique(); + else + d->clearRawData(); - if (!d) { - d = new QJsonPrivate::Data(0, QJsonValue::Array); - } else if (d->compactionCounter || array.a != d->header->root()) { - QJsonArray a(array); - if (d->compactionCounter) - a.compact(); - else - a.detach2(); - d = a.d; - d->ref.ref(); - return; - } - d->ref.ref(); + d->value = QCborValue::fromJsonValue(array); } #if QT_STRINGVIEW_LEVEL < 2 @@ -572,7 +641,7 @@ const QJsonValue QJsonDocument::operator[](QStringView key) const if (!isObject()) return QJsonValue(QJsonValue::Undefined); - return object().value(key); + return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key)); } /*! @@ -584,7 +653,7 @@ const QJsonValue QJsonDocument::operator[](QLatin1String key) const if (!isObject()) return QJsonValue(QJsonValue::Undefined); - return object().value(key); + return QJsonPrivate::Value::fromTrustedCbor(d->value.toMap().value(key)); } /*! @@ -604,7 +673,7 @@ const QJsonValue QJsonDocument::operator[](int i) const if (!isArray()) return QJsonValue(QJsonValue::Undefined); - return array().at(i); + return QJsonPrivate::Value::fromTrustedCbor(d->value.toArray().at(i)); } /*! @@ -612,21 +681,7 @@ const QJsonValue QJsonDocument::operator[](int i) const */ bool QJsonDocument::operator==(const QJsonDocument &other) const { - if (d == other.d) - return true; - - if (!d || !other.d) - return false; - - if (d->header->root()->isArray() != other.d->header->root()->isArray()) - return false; - - if (d->header->root()->isObject()) - return QJsonObject(d, static_cast(d->header->root())) - == QJsonObject(other.d, static_cast(other.d->header->root())); - else - return QJsonArray(d, static_cast(d->header->root())) - == QJsonArray(other.d, static_cast(other.d->header->root())); + return (!d) ? (!other.d) : (d->value == other.d->value); } /*! @@ -658,10 +713,11 @@ QDebug operator<<(QDebug dbg, const QJsonDocument &o) return dbg; } QByteArray json; - if (o.d->header->root()->isArray()) - QJsonPrivate::Writer::arrayToJson(static_cast(o.d->header->root()), json, 0, true); + const QCborContainerPrivate *container = QJsonPrivate::Value::container(o.d->value); + if (o.d->value.isArray()) + QJsonPrivate::Writer::arrayToJson(container, json, 0, true); else - QJsonPrivate::Writer::objectToJson(static_cast(o.d->header->root()), json, 0, true); + QJsonPrivate::Writer::objectToJson(container, json, 0, true); dbg.nospace() << "QJsonDocument(" << json.constData() // print as utf-8 string without extra quotation marks << ')'; diff --git a/src/corelib/serialization/qjsondocument.h b/src/corelib/serialization/qjsondocument.h index a8e7485f5d..325e47b531 100644 --- a/src/corelib/serialization/qjsondocument.h +++ b/src/corelib/serialization/qjsondocument.h @@ -41,14 +41,16 @@ #define QJSONDOCUMENT_H #include +#include + +#include QT_BEGIN_NAMESPACE class QDebug; +class QCborValue; -namespace QJsonPrivate { - class Parser; -} +namespace QJsonPrivate { class Parser; } struct Q_CORE_EXPORT QJsonParseError { @@ -76,6 +78,7 @@ struct Q_CORE_EXPORT QJsonParseError ParseError error; }; +class QJsonDocumentPrivate; class Q_CORE_EXPORT QJsonDocument { public: @@ -93,11 +96,7 @@ public: QJsonDocument(const QJsonDocument &other); QJsonDocument &operator =(const QJsonDocument &other); - QJsonDocument(QJsonDocument &&other) noexcept - : d(other.d) - { - other.d = nullptr; - } + QJsonDocument(QJsonDocument &&other) noexcept; QJsonDocument &operator =(QJsonDocument &&other) noexcept { @@ -105,21 +104,20 @@ public: return *this; } - void swap(QJsonDocument &other) noexcept - { - qSwap(d, other.d); - } + void swap(QJsonDocument &other) noexcept; enum DataValidation { Validate, BypassValidation }; +#if QT_CONFIG(binaryjson) static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate); const char *rawData(int *size) const; static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate); QByteArray toBinaryData() const; +#endif // QT_CONFIG(binaryjson) static QJsonDocument fromVariant(const QVariant &variant); QVariant toVariant() const; @@ -160,13 +158,12 @@ public: private: friend class QJsonValue; - friend class QJsonPrivate::Data; friend class QJsonPrivate::Parser; friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); - QJsonDocument(QJsonPrivate::Data *data); + QJsonDocument(const QCborValue &data); - QJsonPrivate::Data *d; + std::unique_ptr d; }; Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument) diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index 8b095db915..ae37481f31 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -40,11 +40,17 @@ #include #include #include +#include #include #include #include -#include "qjson_p.h" +#include + +#include #include "qjsonwriter_p.h" +#include "qjson_p.h" + +#include QT_BEGIN_NAMESPACE @@ -109,10 +115,7 @@ QT_BEGIN_NAMESPACE \sa isEmpty() */ -QJsonObject::QJsonObject() - : d(nullptr), o(nullptr) -{ -} +QJsonObject::QJsonObject() = default; /*! \fn QJsonObject::QJsonObject(std::initializer_list > args) @@ -131,12 +134,10 @@ QJsonObject::QJsonObject() /*! \internal */ -QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) - : d(data), o(object) +QJsonObject::QJsonObject(QCborContainerPrivate *object) + : o(object) { - Q_ASSERT(d); Q_ASSERT(o); - d->ref.ref(); } /*! @@ -149,17 +150,19 @@ QJsonObject::QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object) void QJsonObject::initialize() { - d = nullptr; o = nullptr; } /*! Destroys the object. */ -QJsonObject::~QJsonObject() +QJsonObject::~QJsonObject() = default; + +QJsonObject::QJsonObject(std::initializer_list > args) { - if (d && !d->ref.deref()) - delete d; + initialize(); + for (const auto &arg : args) + insert(arg.first, arg.second); } /*! @@ -170,10 +173,13 @@ QJsonObject::~QJsonObject() */ QJsonObject::QJsonObject(const QJsonObject &other) { - d = other.d; o = other.o; - if (d) - d->ref.ref(); +} + +QJsonObject::QJsonObject(QJsonObject &&other) noexcept + : o(other.o) +{ + other.o = nullptr; } /*! @@ -181,15 +187,7 @@ QJsonObject::QJsonObject(const QJsonObject &other) */ QJsonObject &QJsonObject::operator =(const QJsonObject &other) { - if (d != other.d) { - if (d && !d->ref.deref()) - delete d; - d = other.d; - if (d) - d->ref.ref(); - } o = other.o; - return *this; } @@ -225,55 +223,7 @@ QJsonObject &QJsonObject::operator =(const QJsonObject &other) */ QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) { - QJsonObject object; - if (map.isEmpty()) - return object; - - object.detach2(1024); - - QVector offsets; - QJsonPrivate::offset currentOffset; - currentOffset = sizeof(QJsonPrivate::Base); - - // the map is already sorted, so we can simply append one entry after the other and - // write the offset table at the end - for (QVariantMap::const_iterator it = map.constBegin(); it != map.constEnd(); ++it) { - QString key = it.key(); - QJsonValue val = QJsonValue::fromVariant(it.value()); - - bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); - - bool latinKey = QJsonPrivate::useCompressed(key); - int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); - int requiredSize = valueOffset + valueSize; - - if (!object.detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry - return QJsonObject(); - - QJsonPrivate::Entry *e = reinterpret_cast(reinterpret_cast(object.o) + currentOffset); - e->value.type = val.t; - e->value.latinKey = latinKey; - e->value.latinOrIntValue = latinOrIntValue; - e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)object.o + valueOffset); - QJsonPrivate::copyString((char *)(e + 1), key, latinKey); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); - - offsets << currentOffset; - currentOffset += requiredSize; - object.o->size = currentOffset; - } - - // write table - object.o->tableOffset = currentOffset; - if (!object.detach2(sizeof(QJsonPrivate::offset)*offsets.size())) - return QJsonObject(); - memcpy(object.o->table(), offsets.constData(), offsets.size()*sizeof(uint)); - object.o->length = offsets.size(); - object.o->size = currentOffset + sizeof(QJsonPrivate::offset)*offsets.size(); - - return object; + return QCborMap::fromVariantMap(map).toJsonObject(); } /*! @@ -285,14 +235,7 @@ QJsonObject QJsonObject::fromVariantMap(const QVariantMap &map) */ QVariantMap QJsonObject::toVariantMap() const { - QVariantMap map; - if (o) { - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - map.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); - } - } - return map; + return QCborMap::fromJsonObject(*this).toVariantMap(); } /*! @@ -324,15 +267,7 @@ QJsonObject QJsonObject::fromVariantHash(const QVariantHash &hash) */ QVariantHash QJsonObject::toVariantHash() const { - QVariantHash hash; - if (o) { - hash.reserve(o->length); - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - hash.insert(e->key(), QJsonValue(d, o, e->value).toVariant()); - } - } - return hash; + return QCborMap::fromJsonObject(*this).toVariantHash(); } /*! @@ -344,11 +279,9 @@ QStringList QJsonObject::keys() const { QStringList keys; if (o) { - keys.reserve(o->length); - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - keys.append(e->key()); - } + keys.reserve(o->elements.length() / 2); + for (int i = 0, end = o->elements.length(); i < end; i += 2) + keys.append(o->stringAt(i)); } return keys; } @@ -358,10 +291,7 @@ QStringList QJsonObject::keys() const */ int QJsonObject::size() const { - if (!d) - return 0; - - return o->length; + return o ? o->elements.length() / 2 : 0; } /*! @@ -371,10 +301,24 @@ int QJsonObject::size() const */ bool QJsonObject::isEmpty() const { - if (!d) - return true; + return !o || o->elements.isEmpty(); +} - return !o->length; +template +static int indexOf(const QExplicitlySharedDataPointer &o, + String key, bool *keyExists) +{ + const auto begin = QJsonPrivate::ConstKeyIterator(o->elements.constBegin()); + const auto end = QJsonPrivate::ConstKeyIterator(o->elements.constEnd()); + + const auto it = std::lower_bound( + begin, end, key, + [&](const QJsonPrivate::ConstKeyIterator::value_type &e, const String &key) { + return o->stringCompareElement(e.key(), key) < 0; + }); + + *keyExists = (it != end) && o->stringEqualsElement((*it).key(), key); + return (it - begin) * 2; } #if QT_STRINGVIEW_LEVEL < 2 @@ -415,14 +359,14 @@ QJsonValue QJsonObject::value(QLatin1String key) const template QJsonValue QJsonObject::valueImpl(T key) const { - if (!d) + if (!o) return QJsonValue(QJsonValue::Undefined); bool keyExists; - int i = o->indexOf(key, &keyExists); + int i = indexOf(o, key, &keyExists); if (!keyExists) return QJsonValue(QJsonValue::Undefined); - return QJsonValue(d, o, o->entryAt(i)->value); + return QJsonPrivate::Value::fromTrustedCbor(o->valueAt(i + 1)); } #if QT_STRINGVIEW_LEVEL < 2 @@ -497,13 +441,16 @@ QJsonValueRef QJsonObject::operator [](QLatin1String key) template QJsonValueRef QJsonObject::atImpl(T key) { + if (!o) + o = new QCborContainerPrivate; + bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; + int index = indexOf(o, key, &keyExists); if (!keyExists) { - iterator i = insertAt(index, key, QJsonValue(), false); - index = i.i; + o->insertAt(index, key); + o->insertAt(index + 1, QCborValue::fromJsonValue(QJsonValue())); } - return QJsonValueRef(this, index); + return QJsonValueRef(this, index / 2); } #if QT_STRINGVIEW_LEVEL < 2 @@ -550,12 +497,12 @@ QJsonObject::iterator QJsonObject::insert(QLatin1String key, const QJsonValue &v template QJsonObject::iterator QJsonObject::insertImpl(T key, const QJsonValue &value) { - if (value.t == QJsonValue::Undefined) { + if (value.type() == QJsonValue::Undefined) { remove(key); return end(); } bool keyExists = false; - int pos = o ? o->indexOf(key, &keyExists) : 0; + int pos = o ? indexOf(o, key, &keyExists) : 0; return insertAt(pos, key, value, keyExists); } @@ -565,40 +512,18 @@ QJsonObject::iterator QJsonObject::insertImpl(T key, const QJsonValue &value) template QJsonObject::iterator QJsonObject::insertAt(int pos, T key, const QJsonValue &value, bool keyExists) { - QJsonValue val = value; - - bool latinOrIntValue; - int valueSize = QJsonPrivate::Value::requiredStorage(val, &latinOrIntValue); - - bool latinKey = QJsonPrivate::useCompressed(key); - int valueOffset = sizeof(QJsonPrivate::Entry) + QJsonPrivate::qStringSize(key, latinKey); - int requiredSize = valueOffset + valueSize; - - if (!detach2(requiredSize + sizeof(QJsonPrivate::offset))) // offset for the new index entry - return iterator(); - - if (!o->length) - o->tableOffset = sizeof(QJsonPrivate::Object); - - if (keyExists) - ++d->compactionCounter; - - uint off = o->reserveSpace(requiredSize, pos, 1, keyExists); - if (!off) - return end(); - - QJsonPrivate::Entry *e = o->entryAt(pos); - e->value.type = val.t; - e->value.latinKey = latinKey; - e->value.latinOrIntValue = latinOrIntValue; - e->value.value = QJsonPrivate::Value::valueToStore(val, (char *)e - (char *)o + valueOffset); - QJsonPrivate::copyString((char *)(e + 1), key, latinKey); - if (valueSize) - QJsonPrivate::Value::copyData(val, (char *)e + valueOffset, latinOrIntValue); - - compactIfNeeded(); + if (o) + detach2(o->elements.length() / 2 + (keyExists ? 0 : 1)); + else + o = new QCborContainerPrivate; - return iterator(this, pos); + if (keyExists) { + o->replaceAt(pos + 1, QCborValue::fromJsonValue(value)); + } else { + o->insertAt(pos, key); + o->insertAt(pos + 1, QCborValue::fromJsonValue(value)); + } + return {this, pos / 2}; } #if QT_STRINGVIEW_LEVEL < 2 @@ -637,11 +562,11 @@ void QJsonObject::remove(QLatin1String key) template void QJsonObject::removeImpl(T key) { - if (!d) + if (!o) return; bool keyExists; - int index = o->indexOf(key, &keyExists); + int index = indexOf(o, key, &keyExists); if (!keyExists) return; @@ -692,13 +617,12 @@ QJsonValue QJsonObject::takeImpl(T key) return QJsonValue(QJsonValue::Undefined); bool keyExists; - int index = o->indexOf(key, &keyExists); + int index = indexOf(o, key, &keyExists); if (!keyExists) return QJsonValue(QJsonValue::Undefined); - QJsonValue v(d, o, o->entryAt(index)->value); + const QJsonValue v = QJsonPrivate::Value::fromTrustedCbor(o->extractAt(index + 1)); removeAt(index); - return v; } @@ -742,7 +666,7 @@ bool QJsonObject::containsImpl(T key) const return false; bool keyExists; - o->indexOf(key, &keyExists); + indexOf(o, key, &keyExists); return keyExists; } @@ -755,16 +679,14 @@ bool QJsonObject::operator==(const QJsonObject &other) const return true; if (!o) - return !other.o->length; + return !other.o->elements.length(); if (!other.o) - return !o->length; - if (o->length != other.o->length) + return !o->elements.length(); + if (o->elements.length() != other.o->elements.length()) return false; - for (uint i = 0; i < o->length; ++i) { - QJsonPrivate::Entry *e = o->entryAt(i); - QJsonValue v(d, o, e->value); - if (other.value(e->key()) != v) + for (int i = 0, end = o->elements.length(); i < end; ++i) { + if (o->valueAt(i) != other.o->valueAt(i)) return false; } @@ -788,9 +710,8 @@ bool QJsonObject::operator!=(const QJsonObject &other) const */ QJsonObject::iterator QJsonObject::erase(QJsonObject::iterator it) { - Q_ASSERT(d && d->ref.loadRelaxed() == 1); - if (it.o != this || it.i < 0 || it.i >= (int)o->length) - return iterator(this, o->length); + if (it.o != this || it.i < 0 || it.i >= o->elements.length()) + return {this, o->elements.length()}; int index = it.i; @@ -839,11 +760,11 @@ template QJsonObject::iterator QJsonObject::findImpl(T key) { bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; + int index = o ? indexOf(o, key, &keyExists) : 0; if (!keyExists) return end(); detach2(); - return iterator(this, index); + return {this, index / 2}; } #if QT_STRINGVIEW_LEVEL < 2 @@ -904,10 +825,10 @@ template QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const { bool keyExists = false; - int index = o ? o->indexOf(key, &keyExists) : 0; + int index = o ? indexOf(o, key, &keyExists) : 0; if (!keyExists) return end(); - return const_iterator(this, index); + return {this, index / 2}; } /*! \fn int QJsonObject::count() const @@ -1500,28 +1421,10 @@ void QJsonObject::detach(uint reserve) bool QJsonObject::detach2(uint reserve) { - if (!d) { - if (reserve >= QJsonPrivate::Value::MaxSize) { - qWarning("QJson: Document too large to store in data structure"); - return false; - } - d = new QJsonPrivate::Data(reserve, QJsonValue::Object); - o = static_cast(d->header->root()); - d->ref.ref(); - return true; - } - if (reserve == 0 && d->ref.loadRelaxed() == 1) + if (!o) return true; - - QJsonPrivate::Data *x = d->clone(o, reserve); - if (!x) - return false; - x->ref.ref(); - if (!d->ref.deref()) - delete d; - d = x; - o = static_cast(d->header->root()); - return true; + o = QCborContainerPrivate::detach(o.data(), reserve ? reserve * 2 : o->elements.length()); + return o; } /*! @@ -1529,21 +1432,11 @@ bool QJsonObject::detach2(uint reserve) */ void QJsonObject::compact() { - if (!d || !d->compactionCounter) + if (!o) return; detach2(); - d->compact(); - o = static_cast(d->header->root()); -} - -/*! - \internal - */ -void QJsonObject::compactIfNeeded() -{ - if (d->compactionCounter > 32u && d->compactionCounter >= unsigned(o->length) / 2u) - compact(); + o->compact(o->elements.length()); } /*! @@ -1551,10 +1444,8 @@ void QJsonObject::compactIfNeeded() */ QString QJsonObject::keyAt(int i) const { - Q_ASSERT(o && i >= 0 && i < (int)o->length); - - QJsonPrivate::Entry *e = o->entryAt(i); - return e->key(); + Q_ASSERT(o && i >= 0 && i * 2 < o->elements.length()); + return o->stringAt(i * 2); } /*! @@ -1562,11 +1453,9 @@ QString QJsonObject::keyAt(int i) const */ QJsonValue QJsonObject::valueAt(int i) const { - if (!o || i < 0 || i >= (int)o->length) + if (!o || i < 0 || 2 * i + 1 >= o->elements.length()) return QJsonValue(QJsonValue::Undefined); - - QJsonPrivate::Entry *e = o->entryAt(i); - return QJsonValue(d, o, e->value); + return QJsonPrivate::Value::fromTrustedCbor(o->valueAt(2 * i + 1)); } /*! @@ -1574,13 +1463,13 @@ QJsonValue QJsonObject::valueAt(int i) const */ void QJsonObject::setValueAt(int i, const QJsonValue &val) { - Q_ASSERT(o && i >= 0 && i < (int)o->length); - - QJsonPrivate::Entry *e = o->entryAt(i); - if (val.t == QJsonValue::Undefined) - removeAt(i); - else - insertAt(i, e->key(), val, true); + Q_ASSERT(o && i >= 0 && 2 * i + 1 < o->elements.length()); + if (val.isUndefined()) { + o->removeAt(2 * i + 1); + o->removeAt(2 * i); + } else { + o->replaceAt(2 * i + 1, QCborValue::fromJsonValue(val)); + } } /*! @@ -1589,9 +1478,8 @@ void QJsonObject::setValueAt(int i, const QJsonValue &val) void QJsonObject::removeAt(int index) { detach2(); - o->removeItems(index, 1); - ++d->compactionCounter; - compactIfNeeded(); + o->removeAt(index + 1); + o->removeAt(index); } uint qHash(const QJsonObject &object, uint seed) @@ -1614,7 +1502,7 @@ QDebug operator<<(QDebug dbg, const QJsonObject &o) return dbg; } QByteArray json; - QJsonPrivate::Writer::objectToJson(o.o, json, 0, true); + QJsonPrivate::Writer::objectToJson(o.o.data(), json, 0, true); dbg.nospace() << "QJsonObject(" << json.constData() // print as utf-8 string without extra quotation marks << ")"; diff --git a/src/corelib/serialization/qjsonobject.h b/src/corelib/serialization/qjsonobject.h index 53db1e1c08..cfb44c9c55 100644 --- a/src/corelib/serialization/qjsonobject.h +++ b/src/corelib/serialization/qjsonobject.h @@ -43,6 +43,7 @@ #include #include #include +#include #include QT_BEGIN_NAMESPACE @@ -53,29 +54,21 @@ typedef QMap QVariantMap; template class QHash; typedef QHash QVariantHash; +class QCborContainerPrivate; + class Q_CORE_EXPORT QJsonObject { public: QJsonObject(); - QJsonObject(std::initializer_list > args) - { - initialize(); - for (std::initializer_list >::const_iterator i = args.begin(); i != args.end(); ++i) - insert(i->first, i->second); - } + QJsonObject(std::initializer_list > args); ~QJsonObject(); QJsonObject(const QJsonObject &other); QJsonObject &operator =(const QJsonObject &other); - QJsonObject(QJsonObject &&other) noexcept - : d(other.d), o(other.o) - { - other.d = nullptr; - other.o = nullptr; - } + QJsonObject(QJsonObject &&other) noexcept; QJsonObject &operator =(QJsonObject &&other) noexcept { @@ -85,7 +78,6 @@ public: void swap(QJsonObject &other) noexcept { - qSwap(d, other.d); qSwap(o, other.o); } @@ -275,20 +267,18 @@ public: inline bool empty() const { return isEmpty(); } private: - friend class QJsonPrivate::Data; friend class QJsonValue; friend class QJsonDocument; friend class QJsonValueRef; - + friend class QCborMap; friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); - QJsonObject(QJsonPrivate::Data *data, QJsonPrivate::Object *object); + QJsonObject(QCborContainerPrivate *object); void initialize(); // ### Qt 6: remove me and merge with detach2 void detach(uint reserve = 0); bool detach2(uint reserve = 0); void compact(); - void compactIfNeeded(); template QJsonValue valueImpl(T key) const; template QJsonValueRef atImpl(T key); @@ -305,8 +295,9 @@ private: void removeAt(int i); template iterator insertAt(int i, T key, const QJsonValue &val, bool exists); - QJsonPrivate::Data *d; - QJsonPrivate::Object *o; + // ### Qt 6: remove + void *dead = nullptr; + QExplicitlySharedDataPointer o; }; Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonObject) diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp index cd36bd5a5b..6d0a92e094 100644 --- a/src/corelib/serialization/qjsonparser.cpp +++ b/src/corelib/serialization/qjsonparser.cpp @@ -45,6 +45,8 @@ #include "qjsonparser_p.h" #include "qjson_p.h" #include "private/qutfcodec_p.h" +#include "private/qcborvalue_p.h" +#include "private/qnumeric_p.h" //#define PARSER_DEBUG #ifdef PARSER_DEBUG @@ -197,9 +199,32 @@ QString QJsonParseError::errorString() const using namespace QJsonPrivate; +class StashedContainer +{ + Q_DISABLE_COPY_MOVE(StashedContainer) +public: + StashedContainer(QExplicitlySharedDataPointer *container, + QCborValue::Type type) + : type(type), stashed(std::move(*container)), current(container) + { + } + + ~StashedContainer() + { + stashed->append(QCborContainerPrivate::makeValue(type, -1, current->take(), + QCborContainerPrivate::MoveContainer)); + *current = std::move(stashed); + } + +private: + QCborValue::Type type; + QExplicitlySharedDataPointer stashed; + QExplicitlySharedDataPointer *current; +}; + Parser::Parser(const char *json, int length) - : head(json), json(json), data(nullptr) - , dataLength(0), current(0), nestingLevel(0) + : head(json), json(json) + , nestingLevel(0) , lastError(QJsonParseError::NoError) { end = json + length; @@ -297,34 +322,30 @@ char Parser::nextToken() /* JSON-text = object / array */ -QJsonDocument Parser::parse(QJsonParseError *error) +QCborValue 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); - Q_CHECK_PTR(data); - - // 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(); + QCborValue data; + DEBUG << Qt::hex << (uint)token; if (token == BeginArray) { + container = new QCborContainerPrivate; if (!parseArray()) goto error; + data = QCborContainerPrivate::makeValue(QCborValue::Array, -1, container.take(), + QCborContainerPrivate::MoveContainer); } else if (token == BeginObject) { + container = new QCborContainerPrivate; if (!parseObject()) goto error; + data = QCborContainerPrivate::makeValue(QCborValue::Map, -1, container.take(), + QCborContainerPrivate::MoveContainer); } else { lastError = QJsonParseError::IllegalValue; goto error; @@ -342,44 +363,95 @@ QJsonDocument Parser::parse(QJsonParseError *error) error->offset = 0; error->error = QJsonParseError::NoError; } - QJsonPrivate::Data *d = new QJsonPrivate::Data(data, current); - return QJsonDocument(d); + + return data; } error: #ifdef PARSER_DEBUG qDebug(">>>>> parser error"); #endif + container.reset(); if (error) { error->offset = json - head; error->error = lastError; } - free(data); - return QJsonDocument(); + return QCborValue(); } -void Parser::ParsedObject::insert(uint offset) { - const QJsonPrivate::Entry *newEntry = reinterpret_cast(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; + +static void sortContainer(QCborContainerPrivate *container) +{ + using Forward = QJsonPrivate::KeyIterator; + using Reverse = std::reverse_iterator; + using Value = Forward::value_type; + + auto compare = [container](const Value &a, const Value &b) + { + const auto &aKey = a.key(); + const auto &bKey = b.key(); + + Q_ASSERT(aKey.flags & QtCbor::Element::HasByteData); + Q_ASSERT(bKey.flags & QtCbor::Element::HasByteData); + + const QtCbor::ByteData *aData = container->byteData(aKey); + const QtCbor::ByteData *bData = container->byteData(bKey); + + if (!aData) + return bData ? -1 : 0; + if (!bData) + return 1; + + // If StringIsAscii is set, we can use either the UTF-8 or the latin1 comparison + // for the string as ASCII is a subset of both. If nothing is set, that means UTF-8. + + // We are currently missing an efficient comparison between UTF-8 and UTF-16 strings. + // Therefore, we need to convert the UTF-8 string if we encounter such a case. + + if (aKey.flags & QtCbor::Element::StringIsAscii) { + if (bKey.flags & QtCbor::Element::StringIsAscii) + return QtPrivate::compareStrings(aData->asLatin1(), bData->asLatin1()); + if (bKey.flags & QtCbor::Element::StringIsUtf16) + return QtPrivate::compareStrings(aData->asLatin1(), bData->asStringView()); + + return QCborContainerPrivate::compareUtf8(aData, bData->asLatin1()); } - } - if (min < offsets.size() && *entryAt(min) == *newEntry) { - offsets[min] = offset; - } else { - offsets.insert(min, offset); - } + + if (aKey.flags & QtCbor::Element::StringIsUtf16) { + if (bKey.flags & QtCbor::Element::StringIsAscii) + return QtPrivate::compareStrings(aData->asStringView(), bData->asLatin1()); + if (bKey.flags & QtCbor::Element::StringIsUtf16) + return QtPrivate::compareStrings(aData->asStringView(), bData->asStringView()); + + // Nasty case. a is UTF-16 and b is UTF-8 + return QtPrivate::compareStrings(aData->asStringView(), bData->toUtf8String()); + } + + if (bKey.flags & QtCbor::Element::StringIsAscii) + return QCborContainerPrivate::compareUtf8(aData, bData->asLatin1()); + + // Nasty case. a is UTF-8 and b is UTF-16 + if (bKey.flags & QtCbor::Element::StringIsUtf16) + return QtPrivate::compareStrings(aData->toUtf8String(), bData->asStringView()); + + return QCborContainerPrivate::compareUtf8(aData, bData->asLatin1()); + }; + + std::sort(Forward(container->elements.begin()), Forward(container->elements.end()), + [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; }); + + // We need to retain the _last_ value for any duplicate keys. Therefore the reverse dance here. + auto it = std::unique(Reverse(container->elements.end()), Reverse(container->elements.begin()), + [&compare](const Value &a, const Value &b) { + return compare(a, b) == 0; + }).base().elementsIterator(); + + // The erase from beginning is expensive but hopefully rare. + container->elements.erase(container->elements.begin(), it); } + /* object = begin-object [ member *( value-separator member ) ] end-object @@ -392,19 +464,14 @@ bool Parser::parseObject() return false; } - int objectOffset = reserveSpace(sizeof(QJsonPrivate::Object)); - if (objectOffset < 0) - return false; - BEGIN << "parseObject pos=" << objectOffset << current << json; - - ParsedObject parsedObject(this, objectOffset); + BEGIN << "parseObject" << json; char token = nextToken(); while (token == Quote) { - int off = current - objectOffset; - if (!parseMember(objectOffset)) + if (!container) + container = new QCborContainerPrivate; + if (!parseMember()) return false; - parsedObject.insert(off); token = nextToken(); if (token != ValueSeparator) break; @@ -421,50 +488,23 @@ bool Parser::parseObject() 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; + + if (container) + sortContainer(container.data()); return true; } /* member = string name-separator value */ -bool Parser::parseMember(int baseOffset) +bool Parser::parseMember() { - int entryOffset = reserveSpace(sizeof(QJsonPrivate::Entry)); - if (entryOffset < 0) - return false; - BEGIN << "parseMember pos=" << entryOffset; + BEGIN << "parseMember"; - bool latin1; - if (!parseString(&latin1)) + if (!parseString()) return false; char token = nextToken(); if (token != NameSeparator) { @@ -475,56 +515,13 @@ bool Parser::parseMember(int baseOffset) lastError = QJsonParseError::UnterminatedObject; return false; } - QJsonPrivate::Value val; - if (!parseValue(&val, baseOffset)) + if (!parseValue()) 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(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(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 */ @@ -537,12 +534,6 @@ bool Parser::parseArray() return false; } - int arrayOffset = reserveSpace(sizeof(QJsonPrivate::Array)); - if (arrayOffset < 0) - return false; - - ValueArray values; - if (!eatSpace()) { lastError = QJsonParseError::UnterminatedArray; return false; @@ -555,13 +546,10 @@ bool Parser::parseArray() lastError = QJsonParseError::UnterminatedArray; return false; } - QJsonPrivate::Value val; - if (!parseValue(&val, arrayOffset)) + if (!container) + container = new QCborContainerPrivate; + if (!parseValue()) return false; - if (!values.append(val)) { - lastError = QJsonParseError::DocumentTooLarge; - return false; - } char token = nextToken(); if (token == EndArray) break; @@ -575,27 +563,11 @@ bool Parser::parseArray() } } - 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; + DEBUG << "size =" << (container ? container->elements.length() : 0); END; --nestingLevel; + return true; } @@ -604,10 +576,9 @@ value = false / null / true / object / array / number / string */ -bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) +bool Parser::parseValue() { BEGIN << "parse Value" << json; - val->_dummy = 0; switch (*json++) { case 'n': @@ -618,7 +589,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) if (*json++ == 'u' && *json++ == 'l' && *json++ == 'l') { - val->type = QJsonValue::Null; + container->append(QCborValue(QCborValue::Null)); DEBUG << "value: null"; END; return true; @@ -633,8 +604,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) if (*json++ == 'r' && *json++ == 'u' && *json++ == 'e') { - val->type = QJsonValue::Bool; - val->value = true; + container->append(QCborValue(true)); DEBUG << "value: true"; END; return true; @@ -650,8 +620,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) *json++ == 'l' && *json++ == 's' && *json++ == 'e') { - val->type = QJsonValue::Bool; - val->value = false; + container->append(QCborValue(false)); DEBUG << "value: false"; END; return true; @@ -659,44 +628,28 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) lastError = QJsonParseError::IllegalValue; return false; case Quote: { - val->type = QJsonValue::String; - if (current - baseOffset >= Value::MaxSize) { - lastError = QJsonParseError::DocumentTooLarge; + if (!parseString()) 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; + case BeginArray: { + StashedContainer stashedContainer(&container, QCborValue::Array); 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; + } + case BeginObject: { + StashedContainer stashedContainer(&container, QCborValue::Map); 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. @@ -708,7 +661,7 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) return false; default: --json; - if (!parseNumber(val, baseOffset)) + if (!parseNumber()) return false; DEBUG << "value: number"; END; @@ -735,10 +688,9 @@ bool Parser::parseValue(QJsonPrivate::Value *val, int baseOffset) */ -bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) +bool Parser::parseNumber() { BEGIN << "parseNumber" << json; - val->type = QJsonValue::Double; const char *start = json; bool isInt = true; @@ -778,42 +730,32 @@ bool Parser::parseNumber(QJsonPrivate::Value *val, int baseOffset) return false; } - QByteArray number(start, json - start); + const QByteArray number = QByteArray::fromRawData(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; + qlonglong n = number.toLongLong(&ok); + if (ok) { + container->append(QCborValue(n)); END; return true; } } bool ok; - union { - quint64 ui; - double d; - }; - d = number.toDouble(&ok); + double 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; + qint64 n; + if (convertDoubleTo(d, &n)) + container->append(QCborValue(n)); + else + container->append(QCborValue(d)); END; return true; @@ -902,58 +844,45 @@ static inline bool scanEscapeSequence(const char *&json, const char *end, uint * static inline bool scanUtf8Char(const char *&json, const char *end, uint *result) { - const uchar *&src = reinterpret_cast(json); - const uchar *uend = reinterpret_cast(end); - uchar b = *src++; - int res = QUtf8Functions::fromUtf8(b, result, src, uend); - if (res < 0) { - // decoding error, backtrack the character we read above - --json; + const auto *usrc = reinterpret_cast(json); + const auto *uend = reinterpret_cast(end); + const uchar b = *usrc++; + int res = QUtf8Functions::fromUtf8(b, result, usrc, uend); + if (res < 0) return false; - } + json = reinterpret_cast(usrc); return true; } -bool Parser::parseString(bool *latin1) +bool Parser::parseString() { - *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; + // try to parse a utf-8 string without escape sequences, and note whether it's 7bit ASCII. - BEGIN << "parse string stringPos=" << stringPos << json; + BEGIN << "parse string" << json; + bool isUtf8 = true; + bool isAscii = true; 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; + if (*json == '\\') { + isAscii = false; + // If we find escape sequences, we store UTF-16 as there are some + // escape sequences which are hard to represent in UTF-8. + // (plain "\\ud800" for example) + isUtf8 = false; break; } - int pos = reserveSpace(1); - if (pos < 0) + if (!scanUtf8Char(json, end, &ch)) { + lastError = QJsonParseError::IllegalUTF8String; return false; - DEBUG << " " << ch << (char)ch; - data[pos] = (uchar)ch; + } + if (ch > 0x7f) + isAscii = false; + DEBUG << " " << ch << char(ch); } ++json; DEBUG << "end of string"; @@ -962,25 +891,20 @@ bool Parser::parseString(bool *latin1) 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; + // no escape sequences, we are done + if (isUtf8) { + container->appendByteData(start, json - start - 1, QCborValue::String, + isAscii ? QtCbor::Element::StringIsAscii + : QtCbor::Element::ValueFlags {}); END; return true; } - *latin1 = false; - DEBUG << "not latin"; + DEBUG << "has escape sequences"; json = start; - current = outStart + sizeof(int); + QString ucs4; while (json < end) { uint ch = 0; if (*json == '"') @@ -997,16 +921,10 @@ bool Parser::parseString(bool *latin1) } } 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); + ucs4.append(QChar::highSurrogate(ch)); + ucs4.append(QChar::lowSurrogate(ch)); } else { - int pos = reserveSpace(2); - if (pos < 0) - return false; - *(QJsonPrivate::qle_ushort *)(data + pos) = (ushort)ch; + ucs4.append(QChar(ushort(ch))); } } ++json; @@ -1016,13 +934,8 @@ bool Parser::parseString(bool *latin1) 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; + container->appendByteData(reinterpret_cast(ucs4.utf16()), ucs4.size() * 2, + QCborValue::String, QtCbor::Element::StringIsUtf16); END; return true; } diff --git a/src/corelib/serialization/qjsonparser_p.h b/src/corelib/serialization/qjsonparser_p.h index 379256847f..14d9705447 100644 --- a/src/corelib/serialization/qjsonparser_p.h +++ b/src/corelib/serialization/qjsonparser_p.h @@ -52,8 +52,8 @@ // #include -#include -#include +#include +#include QT_BEGIN_NAMESPACE @@ -64,25 +64,7 @@ class Parser public: Parser(const char *json, int length); - QJsonDocument parse(QJsonParseError *error); - - class ParsedObject - { - public: - ParsedObject(Parser *p, int pos) : parser(p), objectPosition(pos) { - offsets.reserve(64); - } - void insert(uint offset); - - Parser *parser; - int objectPosition; - QVector offsets; - - inline QJsonPrivate::Entry *entryAt(int i) const { - return reinterpret_cast(parser->data + objectPosition + offsets[i]); - } - }; - + QCborValue parse(QJsonParseError *error); private: inline void eatBOM(); @@ -91,34 +73,17 @@ private: bool parseObject(); bool parseArray(); - bool parseMember(int baseOffset); - bool parseString(bool *latin1); - bool parseValue(QJsonPrivate::Value *val, int baseOffset); - bool parseNumber(QJsonPrivate::Value *val, int baseOffset); + bool parseMember(); + bool parseString(); + bool parseValue(); + bool parseNumber(); const char *head; const char *json; const char *end; - char *data; - int dataLength; - int current; int nestingLevel; QJsonParseError::ParseError lastError; - - inline int reserveSpace(int space) { - if (current + space >= dataLength) { - dataLength = 2*dataLength + space; - char *newData = (char *)realloc(data, dataLength); - if (!newData) { - lastError = QJsonParseError::DocumentTooLarge; - return -1; - } - data = newData; - } - int pos = current; - current += space; - return pos; - } + QExplicitlySharedDataPointer container; }; } diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp index 5f07a6a03e..033e438580 100644 --- a/src/corelib/serialization/qjsonvalue.cpp +++ b/src/corelib/serialization/qjsonvalue.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -47,10 +48,11 @@ #include #include "qdatastream.h" -#ifndef QT_BOOTSTRAPPED -# include -# include -#endif +#include +#include + +#include +#include #include "qjson_p.h" @@ -112,70 +114,61 @@ QT_BEGIN_NAMESPACE The default is to create a Null value. */ QJsonValue::QJsonValue(Type type) - : ui(0), d(nullptr), t(type) + : d(nullptr), t(QCborValue::Undefined) { -} - -/*! - \internal - */ -QJsonValue::QJsonValue(QJsonPrivate::Data *data, QJsonPrivate::Base *base, const QJsonPrivate::Value &v) - : d(nullptr) -{ - t = (Type)(uint)v.type; - switch (t) { - case Undefined: + switch (type) { case Null: - dbl = 0; + t = QCborValue::Null; break; case Bool: - b = v.toBoolean(); + t = QCborValue::False; break; case Double: - dbl = v.toDouble(base); + t = QCborValue::Double; break; - case String: { - QString s = v.toString(base); - stringData = s.data_ptr(); - stringData->ref.ref(); + case String: + t = QCborValue::String; break; - } case Array: + t = QCborValue::Array; + break; case Object: - d = data; - this->base = v.base(base); + t = QCborValue::Map; + break; + case Undefined: break; } - if (d) - d->ref.ref(); } /*! Creates a value of type Bool, with value \a b. */ QJsonValue::QJsonValue(bool b) - : d(nullptr), t(Bool) + : t(b ? QCborValue::True : QCborValue::False) { - this->b = b; } /*! Creates a value of type Double, with value \a n. */ -QJsonValue::QJsonValue(double n) - : d(nullptr), t(Double) +QJsonValue::QJsonValue(double v) + : d(nullptr) { - this->dbl = n; + if (convertDoubleTo(v, &n)) { + t = QCborValue::Integer; + } else { + memcpy(&n, &v, sizeof(n)); + t = QCborValue::Double; + } } /*! \overload Creates a value of type Double, with value \a n. */ -QJsonValue::QJsonValue(int n) - : d(nullptr), t(Double) +QJsonValue::QJsonValue(int v) + : n(v), t(QCborValue::Integer) { - this->dbl = n; } /*! @@ -184,19 +177,17 @@ QJsonValue::QJsonValue(int n) NOTE: the integer limits for IEEE 754 double precision data is 2^53 (-9007199254740992 to +9007199254740992). If you pass in values outside this range expect a loss of precision to occur. */ -QJsonValue::QJsonValue(qint64 n) - : d(nullptr), t(Double) +QJsonValue::QJsonValue(qint64 v) + : n(v), t(QCborValue::Integer) { - this->dbl = double(n); } /*! Creates a value of type String, with value \a s. */ QJsonValue::QJsonValue(const QString &s) - : d(nullptr), t(String) + : QJsonValue(QJsonPrivate::Value::fromTrustedCbor(s)) { - stringDataFromQStringHelper(s); } /*! @@ -211,71 +202,50 @@ QJsonValue::QJsonValue(const QString &s) \since 5.3 */ +// ### Qt6: remove void QJsonValue::stringDataFromQStringHelper(const QString &string) { - stringData = *(QStringData **)(&string); - stringData->ref.ref(); + *this = QJsonValue(string); } /*! Creates a value of type String, with value \a s. */ QJsonValue::QJsonValue(QLatin1String s) - : d(nullptr), t(String) + : QJsonValue(QJsonPrivate::Value::fromTrustedCbor(s)) { - // ### FIXME: Avoid creating the temp QString below - QString str(s); - stringDataFromQStringHelper(str); } /*! Creates a value of type Array, with value \a a. */ QJsonValue::QJsonValue(const QJsonArray &a) - : d(a.d), t(Array) + : n(-1), d(a.a), t(QCborValue::Array) { - base = a.a; - if (d) - d->ref.ref(); } /*! Creates a value of type Object, with value \a o. */ QJsonValue::QJsonValue(const QJsonObject &o) - : d(o.d), t(Object) + : n(-1), d(o.o), t(QCborValue::Map) { - base = o.o; - if (d) - d->ref.ref(); } /*! Destroys the value. */ -QJsonValue::~QJsonValue() -{ - if (t == String && stringData && !stringData->ref.deref()) - free(stringData); - - if (d && !d->ref.deref()) - delete d; -} +QJsonValue::~QJsonValue() = default; /*! Creates a copy of \a other. */ QJsonValue::QJsonValue(const QJsonValue &other) { + n = other.n; t = other.t; d = other.d; - ui = other.ui; - if (d) - d->ref.ref(); - - if (t == String && stringData) - stringData->ref.ref(); } /*! @@ -288,6 +258,23 @@ QJsonValue &QJsonValue::operator =(const QJsonValue &other) return *this; } +QJsonValue::QJsonValue(QJsonValue &&other) noexcept : + n(other.n), + d(other.d), + t(other.t) +{ + other.n = 0; + other.d = nullptr; + other.t = QCborValue::Null; +} + +void QJsonValue::swap(QJsonValue &other) noexcept +{ + qSwap(n, other.n); + qSwap(d, other.d); + qSwap(t, other.t); +} + /*! \fn QJsonValue::QJsonValue(QJsonValue &&other) \since 5.10 @@ -528,23 +515,27 @@ QJsonValue QJsonValue::fromVariant(const QVariant &variant) QVariant QJsonValue::toVariant() const { switch (t) { - case Bool: - return b; - case Double: - return dbl; - case String: + case QCborValue::True: + return true; + case QCborValue::False: + return false; + case QCborValue::Integer: + case QCborValue::Double: + return toDouble(); + case QCborValue::String: return toString(); - case Array: + case QCborValue::Array: return d ? - QJsonArray(d, static_cast(base)).toVariantList() : + QJsonArray(d.data()).toVariantList() : QVariantList(); - case Object: + case QCborValue::Map: return d ? - QJsonObject(d, static_cast(base)).toVariantMap() : + QJsonObject(d.data()).toVariantMap() : QVariantMap(); - case Null: + case QCborValue::Null: return QVariant::fromValue(nullptr); - case Undefined: + case QCborValue::Undefined: + default: break; } return QVariant(); @@ -573,7 +564,25 @@ QVariant QJsonValue::toVariant() const */ QJsonValue::Type QJsonValue::type() const { - return t; + switch (t) { + case QCborValue::Null: + return QJsonValue::Null; + case QCborValue::True: + case QCborValue::False: + return QJsonValue::Bool; + case QCborValue::Double: + case QCborValue::Integer: + return QJsonValue::Double; + case QCborValue::String: + return QJsonValue::String; + case QCborValue::Array: + return QJsonValue::Array; + case QCborValue::Map: + return QJsonValue::Object; + case QCborValue::Undefined: + default: + return QJsonValue::Undefined; + } } /*! @@ -583,9 +592,14 @@ QJsonValue::Type QJsonValue::type() const */ bool QJsonValue::toBool(bool defaultValue) const { - if (t != Bool) + switch (t) { + case QCborValue::True: + return true; + case QCborValue::False: + return false; + default: return defaultValue; - return b; + } } /*! @@ -597,9 +611,20 @@ bool QJsonValue::toBool(bool defaultValue) const */ int QJsonValue::toInt(int defaultValue) const { - if (t == Double && int(dbl) == dbl) - return int(dbl); - return defaultValue; + switch (t) { + case QCborValue::Double: { + const double dbl = toDouble(); + int dblInt; + convertDoubleTo(dbl, &dblInt); + return dbl == dblInt ? dblInt : defaultValue; + } + case QCborValue::Integer: + return (n <= qint64(std::numeric_limits::max()) + && n >= qint64(std::numeric_limits::min())) + ? n : defaultValue; + default: + return defaultValue; + } } /*! @@ -609,9 +634,17 @@ int QJsonValue::toInt(int defaultValue) const */ double QJsonValue::toDouble(double defaultValue) const { - if (t != Double) + switch (t) { + case QCborValue::Double: { + double d; + memcpy(&d, &n, sizeof(d)); + return d; + } + case QCborValue::Integer: + return n; + default: return defaultValue; - return dbl; + } } /*! @@ -621,11 +654,7 @@ double QJsonValue::toDouble(double defaultValue) const */ QString QJsonValue::toString(const QString &defaultValue) const { - if (t != String) - return defaultValue; - stringData->ref.ref(); // the constructor below doesn't add a ref. - QStringDataPtr holder = { stringData }; - return QString(holder); + return (t == QCborValue::String && d) ? d->stringAt(n) : defaultValue; } /*! @@ -637,11 +666,7 @@ QString QJsonValue::toString(const QString &defaultValue) const */ QString QJsonValue::toString() const { - if (t != String) - return QString(); - stringData->ref.ref(); // the constructor below doesn't add a ref. - QStringDataPtr holder = { stringData }; - return QString(holder); + return (t == QCborValue::String && d) ? d->stringAt(n) : QString(); } /*! @@ -651,10 +676,10 @@ QString QJsonValue::toString() const */ QJsonArray QJsonValue::toArray(const QJsonArray &defaultValue) const { - if (!d || t != Array) + if (t != QCborValue::Array || n >= 0 || !d) return defaultValue; - return QJsonArray(d, static_cast(base)); + return QJsonArray(d.data()); } /*! @@ -676,10 +701,10 @@ QJsonArray QJsonValue::toArray() const */ QJsonObject QJsonValue::toObject(const QJsonObject &defaultValue) const { - if (!d || t != Object) + if (t != QCborValue::Map || n >= 0 || !d) return defaultValue; - return QJsonObject(d, static_cast(base)); + return QJsonObject(d.data()); } /*! @@ -766,33 +791,31 @@ bool QJsonValue::operator==(const QJsonValue &other) const return false; switch (t) { - case Undefined: - case Null: + case QCborValue::Undefined: + case QCborValue::Null: + case QCborValue::True: + case QCborValue::False: break; - case Bool: - return b == other.b; - case Double: - return dbl == other.dbl; - case String: + case QCborValue::Double: + return toDouble() == other.toDouble(); + case QCborValue::Integer: + return n == other.n; + case QCborValue::String: return toString() == other.toString(); - case Array: - if (base == other.base) - return true; - if (!base) - return !other.base->length; - if (!other.base) - return !base->length; - return QJsonArray(d, static_cast(base)) - == QJsonArray(other.d, static_cast(other.base)); - case Object: - if (base == other.base) - return true; - if (!base) - return !other.base->length; - if (!other.base) - return !base->length; - return QJsonObject(d, static_cast(base)) - == QJsonObject(other.d, static_cast(other.base)); + case QCborValue::Array: + if (!d) + return !other.d || other.d->elements.length() == 0; + if (!other.d) + return d->elements.length() == 0; + return QJsonArray(d.data()) == QJsonArray(other.d.data()); + case QCborValue::Map: + if (!d) + return !other.d || other.d->elements.length() == 0; + if (!other.d) + return d->elements.length() == 0; + return QJsonObject(d.data()) == QJsonObject(other.d.data()); + default: + return false; } return true; } @@ -810,15 +833,7 @@ bool QJsonValue::operator!=(const QJsonValue &other) const */ void QJsonValue::detach() { - if (!d) - return; - - QJsonPrivate::Data *x = d->clone(base); - x->ref.ref(); - if (!d->ref.deref()) - delete d; - d = x; - base = static_cast(d->header->root()); + d.detach(); } @@ -914,7 +929,7 @@ uint qHash(const QJsonValue &value, uint seed) QDebug operator<<(QDebug dbg, const QJsonValue &o) { QDebugStateSaver saver(dbg); - switch (o.t) { + switch (o.type()) { case QJsonValue::Undefined: dbg << "QJsonValue(undefined)"; break; @@ -948,7 +963,7 @@ QDebug operator<<(QDebug dbg, const QJsonValue &o) #ifndef QT_NO_DATASTREAM QDataStream &operator<<(QDataStream &stream, const QJsonValue &v) { - quint8 type = v.t; + quint8 type = v.type(); stream << type; switch (type) { case QJsonValue::Undefined: diff --git a/src/corelib/serialization/qjsonvalue.h b/src/corelib/serialization/qjsonvalue.h index 8ade18509b..5adcd64176 100644 --- a/src/corelib/serialization/qjsonvalue.h +++ b/src/corelib/serialization/qjsonvalue.h @@ -42,22 +42,18 @@ #include #include +#include +#include QT_BEGIN_NAMESPACE -class QDebug; class QVariant; class QJsonArray; class QJsonObject; +class QCborContainerPrivate; namespace QJsonPrivate { - class Data; - class Base; - class Object; - class Header; - class Array; - class Value; - class Entry; +class Value; } class Q_CORE_EXPORT QJsonValue @@ -77,12 +73,12 @@ public: QJsonValue(bool b); QJsonValue(double n); QJsonValue(int n); - QJsonValue(qint64 n); + QJsonValue(qint64 v); QJsonValue(const QString &s); QJsonValue(QLatin1String s); #ifndef QT_NO_CAST_FROM_ASCII inline QT_ASCII_CAST_WARN QJsonValue(const char *s) - : d(nullptr), t(String) { stringDataFromQStringHelper(QString::fromUtf8(s)); } + : QJsonValue(QString::fromUtf8(s)) {} #endif QJsonValue(const QJsonArray &a); QJsonValue(const QJsonObject &o); @@ -92,15 +88,7 @@ public: QJsonValue(const QJsonValue &other); QJsonValue &operator =(const QJsonValue &other); - QJsonValue(QJsonValue &&other) noexcept - : ui(other.ui), - d(other.d), - t(other.t) - { - other.ui = 0; - other.d = nullptr; - other.t = Null; - } + QJsonValue(QJsonValue &&other) noexcept; QJsonValue &operator =(QJsonValue &&other) noexcept { @@ -108,12 +96,7 @@ public: return *this; } - void swap(QJsonValue &other) noexcept - { - qSwap(ui, other.ui); - qSwap(d, other.d); - qSwap(t, other.t); - } + void swap(QJsonValue &other) noexcept; static QJsonValue fromVariant(const QVariant &variant); QVariant toVariant() const; @@ -149,7 +132,7 @@ public: private: // avoid implicit conversions from char * to bool - inline QJsonValue(const void *) {} + QJsonValue(const void *) = delete; friend class QJsonPrivate::Value; friend class QJsonArray; friend class QJsonObject; @@ -157,20 +140,19 @@ private: friend Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonValue &); - QJsonValue(QJsonPrivate::Data *d, QJsonPrivate::Base *b, const QJsonPrivate::Value& v); + // ### Qt6: Remove this. void stringDataFromQStringHelper(const QString &string); void detach(); - union { - quint64 ui; - bool b; - double dbl; - QStringData *stringData; - QJsonPrivate::Base *base; - }; - QJsonPrivate::Data *d; // needed for Objects and Arrays - Type t; + // ### Qt6: change to an actual QCborValue + qint64 n = 0; + QExplicitlySharedDataPointer d; // needed for Objects, Arrays, Strings + QCborValue::Type t; + + // Assert binary compatibility with pre-5.15 QJsonValue + Q_STATIC_ASSERT(sizeof(QExplicitlySharedDataPointer) == sizeof(void *)); + Q_STATIC_ASSERT(sizeof(QCborValue::Type) == sizeof(QJsonValue::Type)); }; class Q_CORE_EXPORT QJsonValueRef diff --git a/src/corelib/serialization/qjsonwriter.cpp b/src/corelib/serialization/qjsonwriter.cpp index 012d3bea86..627d1bbd62 100644 --- a/src/corelib/serialization/qjsonwriter.cpp +++ b/src/corelib/serialization/qjsonwriter.cpp @@ -44,13 +44,14 @@ #include "qjson_p.h" #include "private/qutfcodec_p.h" #include +#include QT_BEGIN_NAMESPACE using namespace QJsonPrivate; -static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact); -static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact); +static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact); +static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact); static inline uchar hexdig(uint u) { @@ -126,16 +127,20 @@ static QByteArray escapedString(const QString &s) return ba; } -static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value &v, QByteArray &json, int indent, bool compact) +static void valueToJson(const QCborValue &v, QByteArray &json, int indent, bool compact) { - QJsonValue::Type type = (QJsonValue::Type)(uint)v.type; + QCborValue::Type type = v.type(); switch (type) { - case QJsonValue::Bool: - json += v.toBoolean() ? "true" : "false"; + case QCborValue::True: + json += "true"; break; - case QJsonValue::Double: { - const double d = v.toDouble(b); - if (qIsFinite(d)) { // +2 to format to ensure the expected precision + case QCborValue::False: + json += "false"; + break; + case QCborValue::Integer: + case QCborValue::Double: { + const double d = v.toDouble(); + if (qIsFinite(d)) { quint64 absInt; json += QByteArray::number(d, convertDoubleTo(std::abs(d), &absInt) ? 'f' : 'g', QLocale::FloatingPointShortest); @@ -144,42 +149,44 @@ static void valueToJson(const QJsonPrivate::Base *b, const QJsonPrivate::Value & } break; } - case QJsonValue::String: + case QCborValue::String: json += '"'; - json += escapedString(v.toString(b)); + json += escapedString(v.toString()); json += '"'; break; - case QJsonValue::Array: + case QCborValue::Array: json += compact ? "[" : "[\n"; - arrayContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); + arrayContentToJson( + QJsonPrivate::Value::container(v), json, indent + (compact ? 0 : 1), compact); json += QByteArray(4*indent, ' '); json += ']'; break; - case QJsonValue::Object: + case QCborValue::Map: json += compact ? "{" : "{\n"; - objectContentToJson(static_cast(v.base(b)), json, indent + (compact ? 0 : 1), compact); + objectContentToJson( + QJsonPrivate::Value::container(v), json, indent + (compact ? 0 : 1), compact); json += QByteArray(4*indent, ' '); json += '}'; break; - case QJsonValue::Null: + case QCborValue::Null: default: json += "null"; } } -static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) +static void arrayContentToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact) { - if (!a || !a->length) + if (!a || a->elements.empty()) return; QByteArray indentString(4*indent, ' '); - uint i = 0; - while (1) { + qsizetype i = 0; + while (true) { json += indentString; - valueToJson(a, a->at(i), json, indent, compact); + valueToJson(a->valueAt(i), json, indent, compact); - if (++i == a->length) { + if (++i == a->elements.size()) { if (!compact) json += '\n'; break; @@ -190,23 +197,23 @@ static void arrayContentToJson(const QJsonPrivate::Array *a, QByteArray &json, i } -static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) +static void objectContentToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact) { - if (!o || !o->length) + if (!o || o->elements.empty()) return; QByteArray indentString(4*indent, ' '); - uint i = 0; - while (1) { - QJsonPrivate::Entry *e = o->entryAt(i); + qsizetype i = 0; + while (true) { + QCborValue e = o->valueAt(i); json += indentString; json += '"'; - json += escapedString(e->key()); + json += escapedString(o->valueAt(i).toString()); json += compact ? "\":" : "\": "; - valueToJson(o, e->value, json, indent, compact); + valueToJson(o->valueAt(i + 1), json, indent, compact); - if (++i == o->length) { + if ((i += 2) == o->elements.size()) { if (!compact) json += '\n'; break; @@ -216,18 +223,18 @@ static void objectContentToJson(const QJsonPrivate::Object *o, QByteArray &json, } } -void Writer::objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact) +void Writer::objectToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact) { - json.reserve(json.size() + (o ? (int)o->size : 16)); + json.reserve(json.size() + (o ? (int)o->elements.size() : 16)); json += compact ? "{" : "{\n"; objectContentToJson(o, json, indent + (compact ? 0 : 1), compact); json += QByteArray(4*indent, ' '); json += compact ? "}" : "}\n"; } -void Writer::arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact) +void Writer::arrayToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact) { - json.reserve(json.size() + (a ? (int)a->size : 16)); + json.reserve(json.size() + (a ? (int)a->elements.size() : 16)); json += compact ? "[" : "[\n"; arrayContentToJson(a, json, indent + (compact ? 0 : 1), compact); json += QByteArray(4*indent, ' '); diff --git a/src/corelib/serialization/qjsonwriter_p.h b/src/corelib/serialization/qjsonwriter_p.h index 76a8460449..8c263bb7c3 100644 --- a/src/corelib/serialization/qjsonwriter_p.h +++ b/src/corelib/serialization/qjsonwriter_p.h @@ -62,8 +62,8 @@ namespace QJsonPrivate class Writer { public: - static void objectToJson(const QJsonPrivate::Object *o, QByteArray &json, int indent, bool compact = false); - static void arrayToJson(const QJsonPrivate::Array *a, QByteArray &json, int indent, bool compact = false); + static void objectToJson(const QCborContainerPrivate *o, QByteArray &json, int indent, bool compact = false); + static void arrayToJson(const QCborContainerPrivate *a, QByteArray &json, int indent, bool compact = false); }; } diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri index 5310fddd67..7407e20d9e 100644 --- a/src/corelib/serialization/serialization.pri +++ b/src/corelib/serialization/serialization.pri @@ -25,7 +25,6 @@ SOURCES += \ serialization/qcbordiagnostic.cpp \ serialization/qcborvalue.cpp \ serialization/qdatastream.cpp \ - serialization/qjson.cpp \ serialization/qjsoncbor.cpp \ serialization/qjsondocument.cpp \ serialization/qjsonobject.cpp \ @@ -45,6 +44,20 @@ qtConfig(cborstream): { serialization/qcborstream.h } +qtConfig(binaryjson): { + HEADERS += \ + serialization/qbinaryjson_p.h \ + serialization/qbinaryjsonarray_p.h \ + serialization/qbinaryjsonobject_p.h \ + serialization/qbinaryjsonvalue_p.h + + SOURCES += \ + serialization/qbinaryjson.cpp \ + serialization/qbinaryjsonarray.cpp \ + serialization/qbinaryjsonobject.cpp \ + serialization/qbinaryjsonvalue.cpp \ +} + false: SOURCES += \ serialization/qcborarray.cpp \ serialization/qcbormap.cpp diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 9863ff5e69..6230cc081d 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -63,8 +63,9 @@ SOURCES += \ ../../corelib/kernel/qsharedmemory.cpp \ ../../corelib/kernel/qsystemsemaphore.cpp \ ../../corelib/plugin/quuid.cpp \ + ../../corelib/serialization/qcborvalue.cpp \ ../../corelib/serialization/qdatastream.cpp \ - ../../corelib/serialization/qjson.cpp \ + ../../corelib/serialization/qjsoncbor.cpp \ ../../corelib/serialization/qjsondocument.cpp \ ../../corelib/serialization/qjsonobject.cpp \ ../../corelib/serialization/qjsonarray.cpp \ -- cgit v1.2.3