summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--qmake/Makefile.unix10
-rw-r--r--qmake/Makefile.win323
-rw-r--r--qmake/qmake.pro5
-rw-r--r--src/corelib/configure.json6
-rw-r--r--src/corelib/global/qconfig-bootstrapped.h1
-rw-r--r--src/corelib/serialization/qbinaryjson.cpp415
-rw-r--r--src/corelib/serialization/qbinaryjson_p.h617
-rw-r--r--src/corelib/serialization/qbinaryjsonarray.cpp137
-rw-r--r--src/corelib/serialization/qbinaryjsonarray_p.h98
-rw-r--r--src/corelib/serialization/qbinaryjsonobject.cpp149
-rw-r--r--src/corelib/serialization/qbinaryjsonobject_p.h97
-rw-r--r--src/corelib/serialization/qbinaryjsonvalue.cpp155
-rw-r--r--src/corelib/serialization/qbinaryjsonvalue_p.h134
-rw-r--r--src/corelib/serialization/qcborvalue.cpp2
-rw-r--r--src/corelib/serialization/qcborvalue.h4
-rw-r--r--src/corelib/serialization/qjson.cpp451
-rw-r--r--src/corelib/serialization/qjson_p.h789
-rw-r--r--src/corelib/serialization/qjsonarray.cpp239
-rw-r--r--src/corelib/serialization/qjsonarray.h26
-rw-r--r--src/corelib/serialization/qjsoncbor.cpp134
-rw-r--r--src/corelib/serialization/qjsondocument.cpp354
-rw-r--r--src/corelib/serialization/qjsondocument.h27
-rw-r--r--src/corelib/serialization/qjsonobject.cpp320
-rw-r--r--src/corelib/serialization/qjsonobject.h29
-rw-r--r--src/corelib/serialization/qjsonparser.cpp453
-rw-r--r--src/corelib/serialization/qjsonparser_p.h51
-rw-r--r--src/corelib/serialization/qjsonvalue.cpp291
-rw-r--r--src/corelib/serialization/qjsonvalue.h54
-rw-r--r--src/corelib/serialization/qjsonwriter.cpp75
-rw-r--r--src/corelib/serialization/qjsonwriter_p.h4
-rw-r--r--src/corelib/serialization/serialization.pri15
-rw-r--r--src/tools/bootstrap/bootstrap.pro3
32 files changed, 2822 insertions, 2326 deletions
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(<random>) ? 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 <qjsonobject.h>
+#include <qjsonarray.h>
+
+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<Object *>(base);
+ for (uint i = 0; i < o->length; ++i)
+ reserve += o->entryAt(i)->usedStorage(o);
+ } else {
+ auto *a = static_cast<Array *>(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<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);
+
+ uint offset = sizeof(Base);
+ if (b->is_object) {
+ const auto *o = static_cast<const Object *>(base);
+ auto *no = static_cast<Object *>(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<char *>(no) + offset, e->value.data(o), dataSize);
+ ne->value.value = offset;
+ offset += dataSize;
+ }
+ }
+ } else {
+ const auto *a = static_cast<const Array *>(base);
+ auto *na = static_cast<Array *>(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<char *>(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<const Object *>(root)->isValid(maxSize)
+ : static_cast<const Array *>(root)->isValid(maxSize);
+}
+
+QJsonDocument ConstData::toJsonDocument() const
+{
+ const Base *root = header->root();
+ return root->is_object
+ ? QJsonDocument(static_cast<const Object *>(root)->toJsonObject())
+ : QJsonDocument(static_cast<const Array *>(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<char *>(table()) + dataSize, table(), length * sizeof(offset));
+ } else {
+ memmove(reinterpret_cast<char *>(table() + posInTable + numItems) + dataSize,
+ table() + posInTable, (length - posInTable) * sizeof(offset));
+ memmove(reinterpret_cast<char *>(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<const Value *>(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<const Value *>(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<const ushort *>(d)))
+ : (sizeof(int)
+ + sizeof(ushort) * qFromLittleEndian(*reinterpret_cast<const 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);
+}
+
+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<const Array *>(base(b))->toJsonArray();
+ case QJsonValue::Object:
+ return static_cast<const Object *>(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<const Array *>(base(b))->isValid(b->tableOffset - value);
+ case QJsonValue::Object:
+ return isValidValueOffset(value, b->tableOffset)
+ && static_cast<const Object *>(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 <private/qbinaryjsonvalue_p.h>
+#include <private/qendian_p.h>
+
+#include <qjsondocument.h>
+
+#include <limits>
+
+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<typename T>
+using q_littleendian = QLEInteger<T>;
+
+using qle_short = q_littleendian<short>;
+using qle_ushort = q_littleendian<unsigned short>;
+using qle_int = q_littleendian<int>;
+using qle_uint = q_littleendian<unsigned int>;
+
+template<int pos, int width>
+using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
+
+template<int pos, int width>
+using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
+
+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<int>::max();
+
+ quint64 non_int = val & (fraction_mask >> exp);
+ if (non_int)
+ return std::numeric_limits<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(reinterpret_cast<const Data *>(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<Data *>(dest);
+ data->length = str.length();
+ qToLittleEndian<quint16>(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<const ushort *>(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<const QChar *>(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<const Data *>(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<Data *>(dest);
+ data->length = src.length();
+ auto *l = reinterpret_cast<uchar *>(data->latin1);
+ const auto *uc = reinterpret_cast<const ushort *>(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<offset *>(reinterpret_cast<char *>(this) + tableOffset);
+ }
+
+ const offset *table() const
+ {
+ return reinterpret_cast<const offset *>(reinterpret_cast<const char *>(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<const Entry *>(reinterpret_cast<const char *>(this) + table()[i]);
+ }
+
+ Entry *entryAt(uint i)
+ {
+ return reinterpret_cast<Entry *>(reinterpret_cast<char *>(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<const Value *>(table() + i); }
+ Value *at(uint i) { return reinterpret_cast<Value *>(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<const char *>(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<quint64>(reinterpret_cast<const uchar *>(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<const Base *>(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<const char *>(this) + sizeof(Entry));
+ }
+
+ Latin1String shallowLatin1Key() const
+ {
+ Q_ASSERT(value.latinKey);
+ return Latin1String(reinterpret_cast<const char *>(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<Base *>(this + 1); }
+ const Base *root() const { return reinterpret_cast<const Base *>(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<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;
+ }
+
+ ~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<char *>(malloc(size));
+ Q_CHECK_PTR(raw);
+ memcpy(raw + sizeof(Header), b, b->size);
+ auto *h = reinterpret_cast<Header *>(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 <qjsonarray.h>
+
+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<char *>(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<QBinaryJsonPrivate::Array *>(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<QBinaryJsonPrivate::Array *>(d->header->root());
+ return true;
+}
+
+void QBinaryJsonArray::compact()
+{
+ if (!d || !d->compactionCounter)
+ return;
+
+ detach();
+ d->compact();
+ a = static_cast<QBinaryJsonPrivate::Array *>(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 <qjsonobject.h>
+
+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<char *>(e) - reinterpret_cast<char *>(o) + valueOffset);
+ QBinaryJsonPrivate::copyString(reinterpret_cast<char *>(e + 1), key, latinKey);
+ if (valueSize) {
+ QBinaryJsonPrivate::Value::copyData(value, reinterpret_cast<char *>(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<QBinaryJsonPrivate::Object *>(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<QBinaryJsonPrivate::Object *>(d->header->root());
+ return true;
+}
+
+void QBinaryJsonObject::compact()
+{
+ if (!d || !d->compactionCounter)
+ return;
+
+ detach();
+ d->compact();
+ o = static_cast<QBinaryJsonPrivate::Object *>(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 <qjsonarray.h>
+#include <qjsonobject.h>
+
+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<QStringData **>(&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<QBinaryJsonPrivate::Object *>(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 <QtCore/qglobal.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qjsonvalue.h>
+
+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 <qalgorithms.h>
-
-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<Object *>(base);
- for (int i = 0; i < (int)o->length; ++i)
- reserve += o->entryAt(i)->usedStorage(o);
- } else {
- Array *a = static_cast<Array *>(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<Object *>(base);
- Object *no = static_cast<Object *>(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<Array *>(base);
- Array *na = static_cast<Array *>(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<Object *>(root)->isValid(maxSize);
- else
- res = static_cast<Array *>(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<Array *>(base(b))->isValid(b->tableOffset - value);
- case QJsonValue::Object:
- return isValidValueOffset(value, b->tableOffset)
- && static_cast<Object *>(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<QJsonPrivate::Base *>(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 <qjsonobject.h>
#include <qjsonvalue.h>
-#include <qjsondocument.h>
-#include <qjsonarray.h>
-#include <qatomic.h>
-#include <qstring.h>
-#include <qendian.h>
-#include <qnumeric.h>
-
-#include "private/qendian_p.h"
-#include "private/qsimd_p.h"
-
-#include <limits.h>
-#include <limits>
-#include <type_traits>
+#include <qcborvalue.h>
+#include <private/qcborvalue_p.h>
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<typename T>
-using q_littleendian = QLEInteger<T>;
-
-typedef q_littleendian<short> qle_short;
-typedef q_littleendian<unsigned short> qle_ushort;
-typedef q_littleendian<int> qle_int;
-typedef q_littleendian<unsigned int> qle_uint;
-
-template<int pos, int width>
-using qle_bitfield = QLEIntegerBitfield<uint, pos, width>;
-
-template<int pos, int width>
-using qle_signedbitfield = QLEIntegerBitfield<int, pos, width>;
-
-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 <typename T>
-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<quint16>(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<typename Element, typename ElementsIterator>
+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 <typename T>
-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<Entry *>(((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<Element>::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<typename Element, typename ElementsIterator>
+inline ObjectIterator<Element, ElementsIterator> operator+(
+ ObjectIterator<Element, ElementsIterator> a,
+ typename ObjectIterator<Element, ElementsIterator>::difference_type n)
{
- return *(Value *) (table() + i);
+ return {a.elementsIterator() + 2 * n};
}
-
-inline Value &Array::operator [](int i)
+template<typename Element, typename ElementsIterator>
+inline ObjectIterator<Element, ElementsIterator> operator+(
+ int n, ObjectIterator<Element, ElementsIterator> 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<typename Element, typename ElementsIterator>
+inline ObjectIterator<Element, ElementsIterator> operator-(
+ ObjectIterator<Element, ElementsIterator> a,
+ typename ObjectIterator<Element, ElementsIterator>::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<typename Element, typename ElementsIterator>
+inline int operator-(
+ ObjectIterator<Element, ElementsIterator> a,
+ ObjectIterator<Element, ElementsIterator> 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<typename Element, typename ElementsIterator>
+inline bool operator!=(
+ ObjectIterator<Element, ElementsIterator> a,
+ ObjectIterator<Element, ElementsIterator> b)
{
- Q_ASSERT(type == QJsonValue::Bool);
- return value != 0;
+ return a.elementsIterator() != b.elementsIterator();
}
-
-inline double Value::toDouble(const Base *b) const
+template<typename Element, typename ElementsIterator>
+inline bool operator==(
+ ObjectIterator<Element, ElementsIterator> a,
+ ObjectIterator<Element, ElementsIterator> b)
{
- Q_ASSERT(type == QJsonValue::Double);
- if (latinOrIntValue)
- return int_value;
-
- quint64 i = qFromLittleEndian<quint64>((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<QtCbor::Element, QVector<QtCbor::Element>::iterator>;
+using ConstKeyIterator = ObjectIterator<const QtCbor::Element, QVector<QtCbor::Element>::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<Base *>(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<Data *>(this), o);
- }
-
- QJsonArray toArray(Array *a) const
- {
- return QJsonArray(const_cast<Data *>(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 <qjsonobject.h>
#include <qjsonvalue.h>
#include <qjsonarray.h>
+#include <qjsondocument.h>
#include <qstringlist.h>
+#include <qcborarray.h>
#include <qvariant.h>
#include <qdebug.h>
+#include <private/qcborvalue_p.h>
+#include <private/qjson_p.h>
+
#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<QJsonValue> 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<QJsonValue> 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<QJsonPrivate::Value> 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<void *>(array.a->table()),
- static_cast<const void *>(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<QJsonPrivate::Array *>(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<QJsonPrivate::Array *>(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<QJsonPrivate::Array *>(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 <QtCore/qjsonvalue.h>
#include <QtCore/qiterator.h>
+#include <QtCore/qshareddata.h>
#include <initializer_list>
QT_BEGIN_NAMESPACE
@@ -56,25 +57,14 @@ class Q_CORE_EXPORT QJsonArray
public:
QJsonArray();
- QJsonArray(std::initializer_list<QJsonValue> args)
- {
- initialize();
- for (std::initializer_list<QJsonValue>::const_iterator i = args.begin(); i != args.end(); ++i)
- append(*i);
- }
+ QJsonArray(std::initializer_list<QJsonValue> 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<QCborContainerPrivate> 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 <private/qnumeric_p.h>
@@ -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 <qstringlist.h>
#include <qvariant.h>
#include <qdebug.h>
+#include <qcbormap.h>
+#include <qcborarray.h>
+#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 <private/qmemory_p.h>
+
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<QJsonDocumentPrivate>(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<QJsonDocumentPrivate>();
+ 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<QJsonDocumentPrivate>();
+ 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<QBinaryJsonPrivate::ConstData> binaryData
+ = qt_make_unique<QBinaryJsonPrivate::ConstData>(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<int>::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<QBinaryJsonPrivate::ConstData> d
+ = qt_make_unique<QBinaryJsonPrivate::ConstData>(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<QJsonDocumentPrivate>();
+ 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<QJsonPrivate::Array *>(d->header->root())).toVariantList();
- else
- return QJsonObject(d, static_cast<QJsonPrivate::Object *>(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<QJsonPrivate::Array *>(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<QJsonPrivate::Object *>(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<QJsonDocumentPrivate>();
+ result.d->value = val;
+ }
+ return result;
}
/*!
@@ -407,26 +524,6 @@ bool QJsonDocument::isEmpty() const
}
/*!
- 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.
\sa array(), isObject()
@@ -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<QJsonPrivate::Object *>(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<QJsonPrivate::Array *>(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<QJsonDocumentPrivate>();
+ 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<QJsonDocumentPrivate>();
+ 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<QJsonPrivate::Object *>(d->header->root()))
- == QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(other.d->header->root()));
- else
- return QJsonArray(d, static_cast<QJsonPrivate::Array *>(d->header->root()))
- == QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(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<QJsonPrivate::Array *>(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<QJsonPrivate::Object *>(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 <QtCore/qjsonvalue.h>
+#include <QtCore/qscopedpointer.h>
+
+#include <memory>
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<QJsonDocumentPrivate> 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 <qjsonobject.h>
#include <qjsonvalue.h>
#include <qjsonarray.h>
+#include <qjsondocument.h>
#include <qstringlist.h>
#include <qdebug.h>
#include <qvariant.h>
-#include "qjson_p.h"
+#include <qcbormap.h>
+
+#include <private/qcborvalue_p.h>
#include "qjsonwriter_p.h"
+#include "qjson_p.h"
+
+#include <algorithm>
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<QPair<QString, QJsonValue> > 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<QPair<QString, QJsonValue> > 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<QJsonPrivate::offset> 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<QJsonPrivate::Entry *>(reinterpret_cast<char *>(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<typename String>
+static int indexOf(const QExplicitlySharedDataPointer<QCborContainerPrivate> &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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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 <typename T>
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<QJsonPrivate::Object *>(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<QJsonPrivate::Object *>(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<QJsonPrivate::Object *>(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 <QtCore/qjsonvalue.h>
#include <QtCore/qiterator.h>
#include <QtCore/qpair.h>
+#include <QtCore/qshareddata.h>
#include <initializer_list>
QT_BEGIN_NAMESPACE
@@ -53,29 +54,21 @@ typedef QMap<QString, QVariant> QVariantMap;
template <class Key, class T> class QHash;
typedef QHash<QString, QVariant> QVariantHash;
+class QCborContainerPrivate;
+
class Q_CORE_EXPORT QJsonObject
{
public:
QJsonObject();
- QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > args)
- {
- initialize();
- for (std::initializer_list<QPair<QString, QJsonValue> >::const_iterator i = args.begin(); i != args.end(); ++i)
- insert(i->first, i->second);
- }
+ QJsonObject(std::initializer_list<QPair<QString, QJsonValue> > 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 <typename T> QJsonValue valueImpl(T key) const;
template <typename T> QJsonValueRef atImpl(T key);
@@ -305,8 +295,9 @@ private:
void removeAt(int i);
template <typename T> iterator insertAt(int i, T key, const QJsonValue &val, bool exists);
- QJsonPrivate::Data *d;
- QJsonPrivate::Object *o;
+ // ### Qt 6: remove
+ void *dead = nullptr;
+ QExplicitlySharedDataPointer<QCborContainerPrivate> 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<QCborContainerPrivate> *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<QCborContainerPrivate> stashed;
+ QExplicitlySharedDataPointer<QCborContainerPrivate> *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<const QJsonPrivate::Entry *>(parser->data + objectPosition + offset);
- int min = 0;
- int n = offsets.size();
- while (n > 0) {
- int half = n >> 1;
- int middle = min + half;
- if (*entryAt(middle) >= *newEntry) {
- n = half;
- } else {
- min = middle + 1;
- n -= half + 1;
+
+static void sortContainer(QCborContainerPrivate *container)
+{
+ using Forward = QJsonPrivate::KeyIterator;
+ using Reverse = std::reverse_iterator<Forward>;
+ 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<QJsonPrivate::Value *>(malloc(alloc*sizeof(QJsonPrivate::Value)));
- if (!newValues)
- return false;
- memcpy(newValues, data, size*sizeof(QJsonPrivate::Value));
- data = newValues;
- } else {
- void *newValues = realloc(data, alloc * sizeof(QJsonPrivate::Value));
- if (!newValues)
- return false;
- data = static_cast<QJsonPrivate::Value *>(newValues);
- }
- return true;
- }
- bool append(const QJsonPrivate::Value &v) {
- if (alloc == size && !grow())
- return false;
- data[size] = v;
- ++size;
- return true;
- }
-
- QJsonPrivate::Value stackValues[prealloc];
- QJsonPrivate::Value *data;
- int alloc;
- int size;
- };
-}
-
/*
array = begin-array [ value *( value-separator value ) ] end-array
*/
@@ -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<const uchar *&>(json);
- const uchar *uend = reinterpret_cast<const uchar *>(end);
- uchar b = *src++;
- int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, result, src, uend);
- if (res < 0) {
- // decoding error, backtrack the character we read above
- --json;
+ const auto *usrc = reinterpret_cast<const uchar *>(json);
+ const auto *uend = reinterpret_cast<const uchar *>(end);
+ const uchar b = *usrc++;
+ int res = QUtf8Functions::fromUtf8<QUtf8BaseTraits>(b, result, usrc, uend);
+ if (res < 0)
return false;
- }
+ json = reinterpret_cast<const char *>(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<const char *>(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 <QtCore/private/qglobal_p.h>
-#include <qjsondocument.h>
-#include <qvarlengtharray.h>
+#include <QtCore/private/qcborvalue_p.h>
+#include <QtCore/qjsondocument.h>
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<uint> offsets;
-
- inline QJsonPrivate::Entry *entryAt(int i) const {
- return reinterpret_cast<QJsonPrivate::Entry *>(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<QCborContainerPrivate> 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 <qjsonobject.h>
#include <qjsonvalue.h>
#include <qjsonarray.h>
+#include <qjsondocument.h>
#include <qurl.h>
#include <quuid.h>
#include <qvariant.h>
@@ -47,10 +48,11 @@
#include <qdebug.h>
#include "qdatastream.h"
-#ifndef QT_BOOTSTRAPPED
-# include <qcborarray.h>
-# include <qcbormap.h>
-#endif
+#include <private/qnumeric_p.h>
+#include <private/qcborvalue_p.h>
+
+#include <qcborarray.h>
+#include <qcbormap.h>
#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<QJsonPrivate::Array *>(base)).toVariantList() :
+ QJsonArray(d.data()).toVariantList() :
QVariantList();
- case Object:
+ case QCborValue::Map:
return d ?
- QJsonObject(d, static_cast<QJsonPrivate::Object *>(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<int>(dbl, &dblInt);
+ return dbl == dblInt ? dblInt : defaultValue;
+ }
+ case QCborValue::Integer:
+ return (n <= qint64(std::numeric_limits<int>::max())
+ && n >= qint64(std::numeric_limits<int>::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<QJsonPrivate::Array *>(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<QJsonPrivate::Object *>(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<QJsonPrivate::Array *>(base))
- == QJsonArray(other.d, static_cast<QJsonPrivate::Array *>(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<QJsonPrivate::Object *>(base))
- == QJsonObject(other.d, static_cast<QJsonPrivate::Object *>(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<QJsonPrivate::Object *>(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 <QtCore/qglobal.h>
#include <QtCore/qstring.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qcborvalue.h>
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<QCborContainerPrivate> d; // needed for Objects, Arrays, Strings
+ QCborValue::Type t;
+
+ // Assert binary compatibility with pre-5.15 QJsonValue
+ Q_STATIC_ASSERT(sizeof(QExplicitlySharedDataPointer<QCborContainerPrivate>) == 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 <private/qnumeric_p.h>
+#include <private/qcborvalue_p.h>
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<QJsonPrivate::Array *>(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<QJsonPrivate::Object *>(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 \