summaryrefslogtreecommitdiffstats
path: root/src/corelib/serialization
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/serialization')
-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/qcborcommon.cpp328
-rw-r--r--src/corelib/serialization/qcborcommon.h2
-rw-r--r--src/corelib/serialization/qcborcommon_p.h84
-rw-r--r--src/corelib/serialization/qcborstream.h224
-rw-r--r--src/corelib/serialization/qcborstreamreader.cpp (renamed from src/corelib/serialization/qcborstream.cpp)1254
-rw-r--r--src/corelib/serialization/qcborstreamreader.h210
-rw-r--r--src/corelib/serialization/qcborstreamwriter.cpp868
-rw-r--r--src/corelib/serialization/qcborstreamwriter.h130
-rw-r--r--src/corelib/serialization/qcborvalue.cpp149
-rw-r--r--src/corelib/serialization/qcborvalue.h16
-rw-r--r--src/corelib/serialization/qdatastream.cpp1
-rw-r--r--src/corelib/serialization/qdatastream.h20
-rw-r--r--src/corelib/serialization/qjson.cpp451
-rw-r--r--src/corelib/serialization/qjson_p.h789
-rw-r--r--src/corelib/serialization/qjsonarray.cpp235
-rw-r--r--src/corelib/serialization/qjsonarray.h26
-rw-r--r--src/corelib/serialization/qjsoncbor.cpp180
-rw-r--r--src/corelib/serialization/qjsondocument.cpp367
-rw-r--r--src/corelib/serialization/qjsondocument.h33
-rw-r--r--src/corelib/serialization/qjsonobject.cpp421
-rw-r--r--src/corelib/serialization/qjsonobject.h51
-rw-r--r--src/corelib/serialization/qjsonparser.cpp453
-rw-r--r--src/corelib/serialization/qjsonparser_p.h51
-rw-r--r--src/corelib/serialization/qjsonvalue.cpp329
-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/qtextstream.cpp54
-rw-r--r--src/corelib/serialization/qtextstream.h42
-rw-r--r--src/corelib/serialization/serialization.pri36
38 files changed, 4888 insertions, 3851 deletions
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/qcborcommon.cpp b/src/corelib/serialization/qcborcommon.cpp
new file mode 100644
index 0000000000..37fb198744
--- /dev/null
+++ b/src/corelib/serialization/qcborcommon.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** 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$
+**
+****************************************************************************/
+
+#define CBOR_NO_ENCODER_API
+#define CBOR_NO_PARSER_API
+#include "qcborcommon_p.h"
+
+#include <QtCore/qdatastream.h>
+
+QT_BEGIN_NAMESPACE
+
+#include <cborerrorstrings.c>
+
+/*!
+ \headerfile <QtCborCommon>
+
+ \brief The <QtCborCommon> header contains definitions common to both the
+ streaming classes (QCborStreamReader and QCborStreamWriter) and to
+ QCborValue.
+ */
+
+/*!
+ \enum QCborSimpleType
+ \relates <QtCborCommon>
+
+ This enum contains the possible "Simple Types" for CBOR. Simple Types range
+ from 0 to 255 and are types that carry no further value.
+
+ The following values are currently known:
+
+ \value False A "false" boolean.
+ \value True A "true" boolean.
+ \value Null Absence of value (null).
+ \value Undefined Missing or deleted value, usually an error.
+
+ Qt CBOR API supports encoding and decoding any Simple Type, whether one of
+ those above or any other value.
+
+ Applications should only use further values if a corresponding specification
+ has been published, otherwise interpretation and validation by the remote
+ may fail. Values 24 to 31 are reserved and must not be used.
+
+ The current authoritative list is maintained by IANA in the
+ \l{https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xml}{Simple
+ Values registry}.
+
+ \sa QCborStreamWriter::append(QCborSimpleType), QCborStreamReader::isSimpleType(),
+ QCborStreamReader::toSimpleType(), QCborValue::isSimpleType(), QCborValue::toSimpleType()
+ */
+
+#if !defined(QT_NO_DATASTREAM)
+QDataStream &operator<<(QDataStream &ds, QCborSimpleType st)
+{
+ return ds << quint8(st);
+}
+
+QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st)
+{
+ quint8 v;
+ ds >> v;
+ st = QCborSimpleType(v);
+ return ds;
+}
+#endif
+
+/*!
+ \enum QCborTag
+ \relates <QtCborCommon>
+
+ This enum contains no enumeration and is used only to provide type-safe
+ access to a CBOR tag.
+
+ CBOR tags are 64-bit numbers that are attached to generic CBOR types to
+ provide further semantic meaning. QCborTag may be constructed from an
+ enumeration found in QCborKnownTags or directly by providing the numeric
+ representation.
+
+ For example, the following creates a QCborValue containing a byte array
+ tagged with a tag 2.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 0
+
+ \sa QCborKnownTags, QCborStreamWriter::append(QCborTag),
+ QCborStreamReader::isTag(), QCborStreamReader::toTag(),
+ QCborValue::isTag(), QCborValue::tag()
+ */
+
+/*!
+ \enum QCborKnownTags
+ \relates <QtCborCommon>
+
+ This enum contains a list of CBOR tags, known at the time of the Qt
+ implementation. This list is not meant to be complete and contains only
+ tags that are either backed by an RFC or specifically used by the Qt
+ implementation.
+
+ The authoritative list is maintained by IANA in the
+ \l{https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml}{CBOR tag
+ registry}.
+
+ \value DateTimeString A date and time string, formatted according to RFC 3339, as refined
+ by RFC 4287. It is the same format as Qt::ISODate and
+ Qt::ISODateWithMs.
+ \value UnixTime_t A numerical representation of seconds elapsed since
+ 1970-01-01T00:00Z.
+ \value PositiveBignum A positive number of arbitrary length, encoded as a byte array in
+ network byte order. For example, the number 2\sup{64} is represented by
+ a byte array containing the byte value 0x01 followed by 8 zero bytes.
+ \value NegativeBignum A negative number of arbirary length, encoded as the absolute value
+ of that number, minus one. For example, a byte array containing
+ byte value 0x02 followed by 8 zero bytes represents the number
+ -2\sup{65} - 1.
+ \value Decimal A decimal fraction, encoded as an array of two integers: the first
+ is the exponent of the power of 10, the second the integral
+ mantissa. The value 273.15 would be encoded as array \c{[-2, 27315]}.
+ \value Bigfloat Similar to Decimal, but the exponent is a power of 2 instead.
+ \value COSE_Encrypt0 An \c Encrypt0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
+ (CBOR Object Signing and Encryption).
+ \value COSE_Mac0 A \c Mac0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
+ (CBOR Object Signing and Encryption).
+ \value COSE_Sign1 A \c Sign1 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
+ (CBOR Object Signing and Encryption).
+ \value ExpectedBase64url Indicates that the byte array should be encoded using Base64url
+ if the stream is converted to JSON.
+ \value ExpectedBase64 Indicates that the byte array should be encoded using Base64
+ if the stream is converted to JSON.
+ \value ExpectedBase16 Indicates that the byte array should be encoded using Base16 (hex)
+ if the stream is converted to JSON.
+ \value EncodedCbor Indicates that the byte array contains a CBOR stream.
+ \value Url Indicates that the string contains a URL.
+ \value Base64url Indicates that the string contains data encoded using Base64url.
+ \value Base64 Indicates that the string contains data encoded using Base64.
+ \value RegularExpression Indicates that the string contains a Perl-Compatible Regular
+ Expression pattern.
+ \value MimeMessage Indicates that the string contains a MIME message (according to
+ \l{https://tools.ietf.org/html/rfc2045}){RFC 2045}.
+ \value Uuid Indicates that the byte array contains a UUID.
+ \value COSE_Encrypt An \c Encrypt map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
+ (CBOR Object Signing and Encryption).
+ \value COSE_Mac A \c Mac map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
+ (CBOR Object Signing and Encryption).
+ \value COSE_Sign A \c Sign map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
+ (CBOR Object Signing and Encryption).
+ \value Signature No change in interpretation; this tag can be used as the outermost
+ tag in a CBOR stream as the file header.
+
+ The following tags are interpreted by QCborValue during decoding and will
+ produce objects with extended Qt types, and it will use those tags when
+ encoding the same extended types.
+
+ \value DateTimeString \l QDateTime
+ \value UnixTime_t \l QDateTime (only in decoding)
+ \value Url \l QUrl
+ \value Uuid \l QUuid
+
+ Additionally, if a QCborValue containing a QByteArray is tagged using one of
+ \c ExpectedBase64url, \c ExpectedBase64 or \c ExpectedBase16, QCborValue
+ will use the expected encoding when converting to JSON (see
+ QCborValue::toJsonValue).
+
+ \sa QCborTag, QCborStreamWriter::append(QCborTag),
+ QCborStreamReader::isTag(), QCborStreamReader::toTag(),
+ QCborValue::isTag(), QCborValue::tag()
+ */
+
+/*!
+ \class QCborError
+ \inmodule QtCore
+ \relates <QtCborCommon>
+ \reentrant
+ \since 5.12
+
+ \brief The QCborError class holds the error condition found while parsing or
+ validating a CBOR stream.
+
+ \sa QCborStreamReader, QCborValue, QCborParserError
+ */
+
+/*!
+ \enum QCborError::Code
+
+ This enum contains the possible error condition codes.
+
+ \value NoError No error was detected.
+ \value UnknownError An unknown error occurred and no further details are available.
+ \value AdvancePastEnd QCborStreamReader::next() was called but there are no more elements in
+ the current context.
+ \value InputOutputError An I/O error with the QIODevice occurred.
+ \value GarbageAtEnd Data was found in the input stream after the last element.
+ \value EndOfFile The end of the input stream was unexpectedly reached while processing an
+ element.
+ \value UnexpectedBreak The CBOR stream contains a Break where it is not allowed (data is
+ corrupt and the error is not recoverable).
+ \value UnknownType The CBOR stream contains an unknown/unparseable Type (data is corrupt
+ and the and the error is not recoverable).
+ \value IllegalType The CBOR stream contains a known type in a position it is not allowed
+ to exist (data is corrupt and the error is not recoverable).
+ \value IllegalNumber The CBOR stream appears to be encoding a number larger than 64-bit
+ (data is corrupt and the error is not recoverable).
+ \value IllegalSimpleType The CBOR stream contains a Simple Type encoded incorrectly (data is
+ corrupt and the error is not recoverable).
+ \value InvalidUtf8String The CBOR stream contains a text string that does not decode properly
+ as UTF-8 (data is corrupt and the error is not recoverable).
+ \value DataTooLarge CBOR string, map or array is too big and cannot be parsed by Qt
+ (internal limitation, but the error is not recoverable).
+ \value NestingTooDeep Too many levels of arrays or maps encountered while processing the
+ input (internal limitation, but the error is not recoverable).
+ \value UnsupportedType The CBOR stream contains a known type that the implementation does not
+ support (internal limitation, but the error is not recoverable).
+ */
+
+/*!
+ \variable QCborError::c
+ \internal
+ */
+
+/*!
+ \fn QCborError::operator Code() const
+
+ Returns the error code that this QCborError object stores.
+ */
+
+/*!
+ Returns a text string that matches the error code in this QCborError object.
+
+ Note: the string is not translated. Applications whose interface allow users
+ to parse CBOR streams need to provide their own, translated strings.
+
+ \sa QCborError::Code
+ */
+QString QCborError::toString() const
+{
+ switch (c) {
+ case NoError:
+ Q_STATIC_ASSERT(int(NoError) == int(CborNoError));
+ return QString();
+
+ case UnknownError:
+ Q_STATIC_ASSERT(int(UnknownError) == int(CborUnknownError));
+ return QStringLiteral("Unknown error");
+ case AdvancePastEnd:
+ Q_STATIC_ASSERT(int(AdvancePastEnd) == int(CborErrorAdvancePastEOF));
+ return QStringLiteral("Read past end of buffer (more bytes needed)");
+ case InputOutputError:
+ Q_STATIC_ASSERT(int(InputOutputError) == int(CborErrorIO));
+ return QStringLiteral("Input/Output error");
+ case GarbageAtEnd:
+ Q_STATIC_ASSERT(int(GarbageAtEnd) == int(CborErrorGarbageAtEnd));
+ return QStringLiteral("Data found after the end of the stream");
+ case EndOfFile:
+ Q_STATIC_ASSERT(int(EndOfFile) == int(CborErrorUnexpectedEOF));
+ return QStringLiteral("Unexpected end of input data (more bytes needed)");
+ case UnexpectedBreak:
+ Q_STATIC_ASSERT(int(UnexpectedBreak) == int(CborErrorUnexpectedBreak));
+ return QStringLiteral("Invalid CBOR stream: unexpected 'break' byte");
+ case UnknownType:
+ Q_STATIC_ASSERT(int(UnknownType) == int(CborErrorUnknownType));
+ return QStringLiteral("Invalid CBOR stream: unknown type");
+ case IllegalType:
+ Q_STATIC_ASSERT(int(IllegalType) == int(CborErrorIllegalType));
+ return QStringLiteral("Invalid CBOR stream: illegal type found");
+ case IllegalNumber:
+ Q_STATIC_ASSERT(int(IllegalNumber) == int(CborErrorIllegalNumber));
+ return QStringLiteral("Invalid CBOR stream: illegal number encoding (future extension)");
+ case IllegalSimpleType:
+ Q_STATIC_ASSERT(int(IllegalSimpleType) == int(CborErrorIllegalSimpleType));
+ return QStringLiteral("Invalid CBOR stream: illegal simple type");
+ case InvalidUtf8String:
+ Q_STATIC_ASSERT(int(InvalidUtf8String) == int(CborErrorInvalidUtf8TextString));
+ return QStringLiteral("Invalid CBOR stream: invalid UTF-8 text string");
+ case DataTooLarge:
+ Q_STATIC_ASSERT(int(DataTooLarge) == int(CborErrorDataTooLarge));
+ return QStringLiteral("Internal limitation: data set too large");
+ case NestingTooDeep:
+ Q_STATIC_ASSERT(int(NestingTooDeep) == int(CborErrorNestingTooDeep));
+ return QStringLiteral("Internal limitation: data nesting too deep");
+ case UnsupportedType:
+ Q_STATIC_ASSERT(int(UnsupportedType) == int(CborErrorUnsupportedType));
+ return QStringLiteral("Internal limitation: unsupported type");
+ }
+
+ // get the error string from TinyCBOR
+ CborError err = CborError(int(c));
+ return QString::fromLatin1(cbor_error_string(err));
+}
+
+QT_END_NAMESPACE
+
+#ifndef QT_BOOTSTRAPPED
+#include "moc_qcborcommon.cpp"
+#endif
diff --git a/src/corelib/serialization/qcborcommon.h b/src/corelib/serialization/qcborcommon.h
index 3dfe50cd09..bec46399ce 100644
--- a/src/corelib/serialization/qcborcommon.h
+++ b/src/corelib/serialization/qcborcommon.h
@@ -148,6 +148,8 @@ inline uint qHash(QCborTag tag, uint seed = 0)
return qHash(quint64(tag), seed);
}
+enum class QCborNegativeInteger : quint64 {};
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QCborTag)
diff --git a/src/corelib/serialization/qcborcommon_p.h b/src/corelib/serialization/qcborcommon_p.h
new file mode 100644
index 0000000000..9b7f4b7099
--- /dev/null
+++ b/src/corelib/serialization/qcborcommon_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Intel Corporation.
+** 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 QCBORCOMMON_P_H
+#define QCBORCOMMON_P_H
+
+#include "qcborcommon.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.
+//
+
+QT_BEGIN_NAMESPACE
+
+#ifdef QT_NO_DEBUG
+# define NDEBUG 1
+#endif
+#undef assert
+#define assert Q_ASSERT
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wunused-function")
+QT_WARNING_DISABLE_CLANG("-Wunused-function")
+QT_WARNING_DISABLE_CLANG("-Wundefined-internal")
+
+#define CBOR_NO_VALIDATION_API 1
+#define CBOR_NO_PRETTY_API 1
+#define CBOR_API static inline
+#define CBOR_PRIVATE_API static inline
+#define CBOR_INLINE_API static inline
+
+#include <cbor.h>
+
+QT_WARNING_POP
+
+Q_DECLARE_TYPEINFO(CborValue, Q_PRIMITIVE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif // QCBORCOMMON_P_H
diff --git a/src/corelib/serialization/qcborstream.h b/src/corelib/serialization/qcborstream.h
index 7a451e63ac..f2b88820cd 100644
--- a/src/corelib/serialization/qcborstream.h
+++ b/src/corelib/serialization/qcborstream.h
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -40,228 +41,17 @@
#ifndef QCBORSTREAM_H
#define QCBORSTREAM_H
-#include <QtCore/qbytearray.h>
-#include <QtCore/qcborcommon.h>
-#include <QtCore/qfloat16.h>
-#include <QtCore/qscopedpointer.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringview.h>
+#include <QtCore/qglobal.h>
-// See qcborcommon.h for why we check
-#if defined(QT_X11_DEFINES_FOUND)
-# undef True
-# undef False
+#if QT_CONFIG(cborstreamreader)
+#include <QtCore/qcborstreamreader.h>
#endif
-QT_BEGIN_NAMESPACE
-
-class QIODevice;
-
-enum class QCborNegativeInteger : quint64 {};
-
-class QCborStreamWriterPrivate;
-class Q_CORE_EXPORT QCborStreamWriter
-{
-public:
- explicit QCborStreamWriter(QIODevice *device);
- explicit QCborStreamWriter(QByteArray *data);
- ~QCborStreamWriter();
- Q_DISABLE_COPY(QCborStreamWriter)
-
- void setDevice(QIODevice *device);
- QIODevice *device() const;
-
- void append(quint64 u);
- void append(qint64 i);
- void append(QCborNegativeInteger n);
- void append(const QByteArray &ba) { appendByteString(ba.constData(), ba.size()); }
- void append(QLatin1String str);
- void append(QStringView str);
- void append(QCborTag tag);
- void append(QCborKnownTags tag) { append(QCborTag(tag)); }
- void append(QCborSimpleType st);
- void append(std::nullptr_t) { append(QCborSimpleType::Null); }
- void append(qfloat16 f);
- void append(float f);
- void append(double d);
-
- void appendByteString(const char *data, qsizetype len);
- void appendTextString(const char *utf8, qsizetype len);
-
- // convenience
- void append(bool b) { append(b ? QCborSimpleType::True : QCborSimpleType::False); }
- void appendNull() { append(QCborSimpleType::Null); }
- void appendUndefined() { append(QCborSimpleType::Undefined); }
-
-#ifndef Q_QDOC
- // overloads to make normal code not complain
- void append(int i) { append(qint64(i)); }
- void append(uint u) { append(quint64(u)); }
+#if QT_CONFIG(cborstreamwriter)
+#include <QtCore/qcborstreamwriter.h>
#endif
-#ifndef QT_NO_CAST_FROM_ASCII
- void append(const char *str, qsizetype size = -1)
- { appendTextString(str, (str && size == -1) ? int(strlen(str)) : size); }
-#endif
-
- void startArray();
- void startArray(quint64 count);
- bool endArray();
- void startMap();
- void startMap(quint64 count);
- bool endMap();
-
- // no API for encoding chunked strings
-
-private:
- QScopedPointer<QCborStreamWriterPrivate> d;
-};
-
-class QCborStreamReaderPrivate;
-class Q_CORE_EXPORT QCborStreamReader
-{
- Q_GADGET
-public:
- enum Type : quint8 {
- UnsignedInteger = 0x00,
- NegativeInteger = 0x20,
- ByteString = 0x40,
- ByteArray = ByteString,
- TextString = 0x60,
- String = TextString,
- Array = 0x80,
- Map = 0xa0,
- Tag = 0xc0,
- SimpleType = 0xe0,
- HalfFloat = 0xf9,
- Float16 = HalfFloat,
- Float = 0xfa,
- Double = 0xfb,
-
- Invalid = 0xff
- };
- Q_ENUM(Type)
-
- enum StringResultCode {
- EndOfString = 0,
- Ok = 1,
- Error = -1
- };
- template <typename Container> struct StringResult {
- Container data;
- StringResultCode status = Error;
- };
- Q_ENUM(StringResultCode)
-
- QCborStreamReader();
- QCborStreamReader(const char *data, qsizetype len);
- QCborStreamReader(const quint8 *data, qsizetype len);
- explicit QCborStreamReader(const QByteArray &data);
- explicit QCborStreamReader(QIODevice *device);
- ~QCborStreamReader();
- Q_DISABLE_COPY(QCborStreamReader)
-
- void setDevice(QIODevice *device);
- QIODevice *device() const;
- void addData(const QByteArray &data);
- void addData(const char *data, qsizetype len);
- void addData(const quint8 *data, qsizetype len)
- { addData(reinterpret_cast<const char *>(data), len); }
- void reparse();
- void clear();
- void reset();
-
- QCborError lastError();
-
- qint64 currentOffset() const;
-
- bool isValid() const { return !isInvalid(); }
-
- int containerDepth() const;
- QCborStreamReader::Type parentContainerType() const;
- bool hasNext() const noexcept Q_DECL_PURE_FUNCTION;
- bool next(int maxRecursion = 10000);
-
- Type type() const { return QCborStreamReader::Type(type_); }
- bool isUnsignedInteger() const { return type() == UnsignedInteger; }
- bool isNegativeInteger() const { return type() == NegativeInteger; }
- bool isInteger() const { return quint8(type()) <= quint8(NegativeInteger); }
- bool isByteArray() const { return type() == ByteArray; }
- bool isString() const { return type() == String; }
- bool isArray() const { return type() == Array; }
- bool isMap() const { return type() == Map; }
- bool isTag() const { return type() == Tag; }
- bool isSimpleType() const { return type() == SimpleType; }
- bool isFloat16() const { return type() == Float16; }
- bool isFloat() const { return type() == Float; }
- bool isDouble() const { return type() == Double; }
- bool isInvalid() const { return type() == Invalid; }
-
- bool isSimpleType(QCborSimpleType st) const { return isSimpleType() && toSimpleType() == st; }
- bool isFalse() const { return isSimpleType(QCborSimpleType::False); }
- bool isTrue() const { return isSimpleType(QCborSimpleType::True); }
- bool isBool() const { return isFalse() || isTrue(); }
- bool isNull() const { return isSimpleType(QCborSimpleType::Null); }
- bool isUndefined() const { return isSimpleType(QCborSimpleType::Undefined); }
-
- bool isLengthKnown() const noexcept Q_DECL_PURE_FUNCTION;
- quint64 length() const;
-
- bool isContainer() const { return isMap() || isArray(); }
- bool enterContainer() { Q_ASSERT(isContainer()); return _enterContainer_helper(); }
- bool leaveContainer();
-
- StringResult<QString> readString() { Q_ASSERT(isString()); return _readString_helper(); }
- StringResult<QByteArray> readByteArray(){ Q_ASSERT(isByteArray()); return _readByteArray_helper(); }
- qsizetype currentStringChunkSize() const{ Q_ASSERT(isString() || isByteArray()); return _currentStringChunkSize(); }
- StringResult<qsizetype> readStringChunk(char *ptr, qsizetype maxlen);
-
- bool toBool() const { Q_ASSERT(isBool()); return value64 - int(QCborSimpleType::False); }
- QCborTag toTag() const { Q_ASSERT(isTag()); return QCborTag(value64); }
- quint64 toUnsignedInteger() const { Q_ASSERT(isUnsignedInteger()); return value64; }
- QCborNegativeInteger toNegativeInteger() const { Q_ASSERT(isNegativeInteger()); return QCborNegativeInteger(value64 + 1); }
- QCborSimpleType toSimpleType() const{ Q_ASSERT(isSimpleType()); return QCborSimpleType(value64); }
- qfloat16 toFloat16() const { Q_ASSERT(isFloat16()); return _toFloatingPoint<qfloat16>(); }
- float toFloat() const { Q_ASSERT(isFloat()); return _toFloatingPoint<float>(); }
- double toDouble() const { Q_ASSERT(isDouble()); return _toFloatingPoint<double>(); }
-
- qint64 toInteger() const
- {
- Q_ASSERT(isInteger());
- qint64 v = qint64(value64);
- if (isNegativeInteger())
- return -v - 1;
- return v;
- }
-
-private:
- void preparse();
- bool _enterContainer_helper();
- StringResult<QString> _readString_helper();
- StringResult<QByteArray> _readByteArray_helper();
- qsizetype _currentStringChunkSize() const;
-
- template <typename FP> FP _toFloatingPoint() const noexcept
- {
- using UIntFP = typename QIntegerForSizeof<FP>::Unsigned;
- UIntFP u = UIntFP(value64);
- FP f;
- memcpy(static_cast<void *>(&f), &u, sizeof(f));
- return f;
- }
-
- friend QCborStreamReaderPrivate;
- friend class QCborContainerPrivate;
- quint64 value64;
- QScopedPointer<QCborStreamReaderPrivate> d;
- quint8 type_;
- quint8 reserved[3] = {};
-};
+QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
-#if defined(QT_X11_DEFINES_FOUND)
-# define True 1
-# define False 0
-#endif
-
#endif // QCBORSTREAM_H
diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstreamreader.cpp
index c598eee213..c983436606 100644
--- a/src/corelib/serialization/qcborstream.cpp
+++ b/src/corelib/serialization/qcborstreamreader.cpp
@@ -37,84 +37,46 @@
**
****************************************************************************/
-#include "qcborstream.h"
+#include "qcborstreamreader.h"
+
+#define CBOR_NO_ENCODER_API
+#include <private/qcborcommon_p.h>
#include <private/qnumeric_p.h>
#include <private/qutfcodec_p.h>
-#include <qbuffer.h>
#include <qdebug.h>
#include <qstack.h>
-#include <qdatastream.h>
QT_BEGIN_NAMESPACE
-#ifdef QT_NO_DEBUG
-# define NDEBUG 1
-#endif
-#undef assert
-#define assert Q_ASSERT
-
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_GCC("-Wunused-function")
-QT_WARNING_DISABLE_CLANG("-Wunused-function")
-QT_WARNING_DISABLE_CLANG("-Wundefined-internal")
-QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
-
-#define CBOR_ENCODER_NO_CHECK_USER
-
-#define CBOR_NO_VALIDATION_API 1
-#define CBOR_NO_PRETTY_API 1
-#define CBOR_API static inline
-#define CBOR_PRIVATE_API static inline
-#define CBOR_INLINE_API static inline
-
-#include <cbor.h>
-
-static CborError qt_cbor_encoder_write_callback(void *token, const void *data, size_t len, CborEncoderAppendType);
static bool qt_cbor_decoder_can_read(void *token, size_t len);
static void qt_cbor_decoder_advance(void *token, size_t len);
static void *qt_cbor_decoder_read(void *token, void *userptr, size_t offset, size_t len);
static CborError qt_cbor_decoder_transfer_string(void *token, const void **userptr, size_t offset, size_t len);
-#define CBOR_ENCODER_WRITER_CONTROL 1
-#define CBOR_ENCODER_WRITE_FUNCTION qt_cbor_encoder_write_callback
-
#define CBOR_PARSER_READER_CONTROL 1
#define CBOR_PARSER_CAN_READ_BYTES_FUNCTION qt_cbor_decoder_can_read
#define CBOR_PARSER_ADVANCE_BYTES_FUNCTION qt_cbor_decoder_advance
#define CBOR_PARSER_TRANSFER_STRING_FUNCTION qt_cbor_decoder_transfer_string
#define CBOR_PARSER_READ_BYTES_FUNCTION qt_cbor_decoder_read
-#include "../3rdparty/tinycbor/src/cborencoder.c"
-#include "../3rdparty/tinycbor/src/cborerrorstrings.c"
-#include "../3rdparty/tinycbor/src/cborparser.c"
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
+
+#include <cborparser.c>
+
+QT_WARNING_POP
-// silence compilers that complain about this being a static function declared
-// but never defined
-static CborError cbor_encoder_close_container_checked(CborEncoder*, const CborEncoder*)
-{
- Q_UNREACHABLE();
- return CborErrorInternalError;
-}
static CborError _cbor_value_dup_string(const CborValue *, void **, size_t *, CborValue *)
{
Q_UNREACHABLE();
return CborErrorInternalError;
}
-static CborError cbor_value_get_half_float_as_float(const CborValue *, float *)
-{
- Q_UNREACHABLE();
- return CborErrorInternalError;
-}
-static CborError cbor_encode_float_as_half_float(CborEncoder *, float)
+static CborError Q_DECL_UNUSED cbor_value_get_half_float_as_float(const CborValue *, float *)
{
Q_UNREACHABLE();
return CborErrorInternalError;
}
-QT_WARNING_POP
-
-Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE);
-Q_DECLARE_TYPEINFO(CborValue, Q_PRIMITIVE_TYPE);
// confirm our constants match TinyCBOR's
Q_STATIC_ASSERT(int(QCborStreamReader::UnsignedInteger) == CborIntegerType);
@@ -130,1195 +92,6 @@ Q_STATIC_ASSERT(int(QCborStreamReader::Double) == CborDoubleType);
Q_STATIC_ASSERT(int(QCborStreamReader::Invalid) == CborInvalidType);
/*!
- \headerfile <QtCborCommon>
-
- \brief The <QtCborCommon> header contains definitions common to both the
- streaming classes (QCborStreamReader and QCborStreamWriter) and to
- QCborValue.
- */
-
-/*!
- \enum QCborSimpleType
- \relates <QtCborCommon>
-
- This enum contains the possible "Simple Types" for CBOR. Simple Types range
- from 0 to 255 and are types that carry no further value.
-
- The following values are currently known:
-
- \value False A "false" boolean.
- \value True A "true" boolean.
- \value Null Absence of value (null).
- \value Undefined Missing or deleted value, usually an error.
-
- Qt CBOR API supports encoding and decoding any Simple Type, whether one of
- those above or any other value.
-
- Applications should only use further values if a corresponding specification
- has been published, otherwise interpretation and validation by the remote
- may fail. Values 24 to 31 are reserved and must not be used.
-
- The current authoritative list is maintained by IANA in the
- \l{https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xml}{Simple
- Values registry}.
-
- \sa QCborStreamWriter::append(QCborSimpleType), QCborStreamReader::isSimpleType(),
- QCborStreamReader::toSimpleType(), QCborValue::isSimpleType(), QCborValue::toSimpleType()
- */
-
-Q_CORE_EXPORT const char *qt_cbor_simpletype_id(QCborSimpleType st)
-{
- switch (st) {
- case QCborSimpleType::False:
- return "False";
- case QCborSimpleType::True:
- return "True";
- case QCborSimpleType::Null:
- return "Null";
- case QCborSimpleType::Undefined:
- return "Undefined";
- }
- return nullptr;
-}
-
-#if !defined(QT_NO_DATASTREAM)
-QDataStream &operator<<(QDataStream &ds, QCborSimpleType st)
-{
- return ds << quint8(st);
-}
-
-QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st)
-{
- quint8 v;
- ds >> v;
- st = QCborSimpleType(v);
- return ds;
-}
-#endif
-
-#if !defined(QT_NO_DEBUG_STREAM)
-QDebug operator<<(QDebug dbg, QCborSimpleType st)
-{
- QDebugStateSaver saver(dbg);
- const char *id = qt_cbor_simpletype_id(st);
- if (id)
- return dbg.nospace() << "QCborSimpleType::" << id;
-
- return dbg.nospace() << "QCborSimpleType(" << uint(st) << ')';
-}
-#endif
-
-/*!
- \enum QCborTag
- \relates <QtCborCommon>
-
- This enum contains no enumeration and is used only to provide type-safe
- access to a CBOR tag.
-
- CBOR tags are 64-bit numbers that are attached to generic CBOR types to
- provide further semantic meaning. QCborTag may be constructed from an
- enumeration found in QCborKnownTags or directly by providing the numeric
- representation.
-
- For example, the following creates a QCborValue containing a byte array
- tagged with a tag 2.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 0
-
- \sa QCborKnownTags, QCborStreamWriter::append(QCborTag),
- QCborStreamReader::isTag(), QCborStreamReader::toTag(),
- QCborValue::isTag(), QCborValue::tag()
- */
-
-Q_CORE_EXPORT const char *qt_cbor_tag_id(QCborTag tag)
-{
- // Casting to QCborKnownTags's underlying type will make the comparison
- // below fail if the tag value is out of range.
- auto n = std::underlying_type<QCborKnownTags>::type(tag);
- if (QCborTag(n) == tag) {
- switch (QCborKnownTags(n)) {
- case QCborKnownTags::DateTimeString:
- return "DateTimeString";
- case QCborKnownTags::UnixTime_t:
- return "UnixTime_t";
- case QCborKnownTags::PositiveBignum:
- return "PositiveBignum";
- case QCborKnownTags::NegativeBignum:
- return "NegativeBignum";
- case QCborKnownTags::Decimal:
- return "Decimal";
- case QCborKnownTags::Bigfloat:
- return "Bigfloat";
- case QCborKnownTags::COSE_Encrypt0:
- return "COSE_Encrypt0";
- case QCborKnownTags::COSE_Mac0:
- return "COSE_Mac0";
- case QCborKnownTags::COSE_Sign1:
- return "COSE_Sign1";
- case QCborKnownTags::ExpectedBase64url:
- return "ExpectedBase64url";
- case QCborKnownTags::ExpectedBase64:
- return "ExpectedBase64";
- case QCborKnownTags::ExpectedBase16:
- return "ExpectedBase16";
- case QCborKnownTags::EncodedCbor:
- return "EncodedCbor";
- case QCborKnownTags::Url:
- return "Url";
- case QCborKnownTags::Base64url:
- return "Base64url";
- case QCborKnownTags::Base64:
- return "Base64";
- case QCborKnownTags::RegularExpression:
- return "RegularExpression";
- case QCborKnownTags::MimeMessage:
- return "MimeMessage";
- case QCborKnownTags::Uuid:
- return "Uuid";
- case QCborKnownTags::COSE_Encrypt:
- return "COSE_Encrypt";
- case QCborKnownTags::COSE_Mac:
- return "COSE_Mac";
- case QCborKnownTags::COSE_Sign:
- return "COSE_Sign";
- case QCborKnownTags::Signature:
- return "Signature";
- }
- }
- return nullptr;
-}
-
-#if !defined(QT_NO_DEBUG_STREAM)
-QDebug operator<<(QDebug dbg, QCborTag tag)
-{
- QDebugStateSaver saver(dbg);
- const char *id = qt_cbor_tag_id(tag);
- dbg.nospace() << "QCborTag(";
- if (id)
- dbg.nospace() << "QCborKnownTags::" << id;
- else
- dbg.nospace() << quint64(tag);
-
- return dbg << ')';
-}
-#endif
-
-/*!
- \enum QCborKnownTags
- \relates <QtCborCommon>
-
- This enum contains a list of CBOR tags, known at the time of the Qt
- implementation. This list is not meant to be complete and contains only
- tags that are either backed by an RFC or specifically used by the Qt
- implementation.
-
- The authoritative list is maintained by IANA in the
- \l{https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml}{CBOR tag
- registry}.
-
- \value DateTimeString A date and time string, formatted according to RFC 3339, as refined
- by RFC 4287. It is the same format as Qt::ISODate and
- Qt::ISODateWithMs.
- \value UnixTime_t A numerical representation of seconds elapsed since
- 1970-01-01T00:00Z.
- \value PositiveBignum A positive number of arbitrary length, encoded as a byte array in
- network byte order. For example, the number 2\sup{64} is represented by
- a byte array containing the byte value 0x01 followed by 8 zero bytes.
- \value NegativeBignum A negative number of arbirary length, encoded as the absolute value
- of that number, minus one. For example, a byte array containing
- byte value 0x02 followed by 8 zero bytes represents the number
- -2\sup{65} - 1.
- \value Decimal A decimal fraction, encoded as an array of two integers: the first
- is the exponent of the power of 10, the second the integral
- mantissa. The value 273.15 would be encoded as array \c{[-2, 27315]}.
- \value Bigfloat Similar to Decimal, but the exponent is a power of 2 instead.
- \value COSE_Encrypt0 An \c Encrypt0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
- (CBOR Object Signing and Encryption).
- \value COSE_Mac0 A \c Mac0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
- (CBOR Object Signing and Encryption).
- \value COSE_Sign1 A \c Sign1 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
- (CBOR Object Signing and Encryption).
- \value ExpectedBase64url Indicates that the byte array should be encoded using Base64url
- if the stream is converted to JSON.
- \value ExpectedBase64 Indicates that the byte array should be encoded using Base64
- if the stream is converted to JSON.
- \value ExpectedBase16 Indicates that the byte array should be encoded using Base16 (hex)
- if the stream is converted to JSON.
- \value EncodedCbor Indicates that the byte array contains a CBOR stream.
- \value Url Indicates that the string contains a URL.
- \value Base64url Indicates that the string contains data encoded using Base64url.
- \value Base64 Indicates that the string contains data encoded using Base64.
- \value RegularExpression Indicates that the string contains a Perl-Compatible Regular
- Expression pattern.
- \value MimeMessage Indicates that the string contains a MIME message (according to
- \l{https://tools.ietf.org/html/rfc2045}){RFC 2045}.
- \value Uuid Indicates that the byte array contains a UUID.
- \value COSE_Encrypt An \c Encrypt map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
- (CBOR Object Signing and Encryption).
- \value COSE_Mac A \c Mac map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
- (CBOR Object Signing and Encryption).
- \value COSE_Sign A \c Sign map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152}
- (CBOR Object Signing and Encryption).
- \value Signature No change in interpretation; this tag can be used as the outermost
- tag in a CBOR stream as the file header.
-
- The following tags are interpreted by QCborValue during decoding and will
- produce objects with extended Qt types, and it will use those tags when
- encoding the same extended types.
-
- \value DateTimeString \l QDateTime
- \value UnixTime_t \l QDateTime (only in decoding)
- \value Url \l QUrl
- \value Uuid \l QUuid
-
- Additionally, if a QCborValue containing a QByteArray is tagged using one of
- \c ExpectedBase64url, \c ExpectedBase64 or \c ExpectedBase16, QCborValue
- will use the expected encoding when converting to JSON (see
- QCborValue::toJsonValue).
-
- \sa QCborTag, QCborStreamWriter::append(QCborTag),
- QCborStreamReader::isTag(), QCborStreamReader::toTag(),
- QCborValue::isTag(), QCborValue::tag()
- */
-
-#if !defined(QT_NO_DEBUG_STREAM)
-QDebug operator<<(QDebug dbg, QCborKnownTags tag)
-{
- QDebugStateSaver saver(dbg);
- const char *id = qt_cbor_tag_id(QCborTag(int(tag)));
- if (id)
- return dbg.nospace() << "QCborKnownTags::" << id;
-
- return dbg.nospace() << "QCborKnownTags(" << int(tag) << ')';
-}
-#endif
-
-/*!
- \class QCborError
- \inmodule QtCore
- \relates <QtCborCommon>
- \reentrant
- \since 5.12
-
- \brief The QCborError class holds the error condition found while parsing or
- validating a CBOR stream.
-
- \sa QCborStreamReader, QCborValue, QCborParserError
- */
-
-/*!
- \enum QCborError::Code
-
- This enum contains the possible error condition codes.
-
- \value NoError No error was detected.
- \value UnknownError An unknown error occurred and no further details are available.
- \value AdvancePastEnd QCborStreamReader::next() was called but there are no more elements in
- the current context.
- \value InputOutputError An I/O error with the QIODevice occurred.
- \value GarbageAtEnd Data was found in the input stream after the last element.
- \value EndOfFile The end of the input stream was unexpectedly reached while processing an
- element.
- \value UnexpectedBreak The CBOR stream contains a Break where it is not allowed (data is
- corrupt and the error is not recoverable).
- \value UnknownType The CBOR stream contains an unknown/unparseable Type (data is corrupt
- and the and the error is not recoverable).
- \value IllegalType The CBOR stream contains a known type in a position it is not allowed
- to exist (data is corrupt and the error is not recoverable).
- \value IllegalNumber The CBOR stream appears to be encoding a number larger than 64-bit
- (data is corrupt and the error is not recoverable).
- \value IllegalSimpleType The CBOR stream contains a Simple Type encoded incorrectly (data is
- corrupt and the error is not recoverable).
- \value InvalidUtf8String The CBOR stream contains a text string that does not decode properly
- as UTF-8 (data is corrupt and the error is not recoverable).
- \value DataTooLarge CBOR string, map or array is too big and cannot be parsed by Qt
- (internal limitation, but the error is not recoverable).
- \value NestingTooDeep Too many levels of arrays or maps encountered while processing the
- input (internal limitation, but the error is not recoverable).
- \value UnsupportedType The CBOR stream contains a known type that the implementation does not
- support (internal limitation, but the error is not recoverable).
- */
-
-// Convert from CborError to QCborError.
-//
-// Centralized in a function in case we need to make more adjustments in the
-// future.
-static QCborError fromCborError(CborError err)
-{
- return { QCborError::Code(int(err)) };
-}
-
-// Convert to CborError from QCborError.
-//
-// Centralized in a function in case we need to make more adjustments in the
-// future.
-static CborError toCborError(QCborError c)
-{
- return CborError(int(c.c));
-}
-
-/*!
- \variable QCborError::c
- \internal
- */
-
-/*!
- \fn QCborError::operator Code() const
-
- Returns the error code that this QCborError object stores.
- */
-
-/*!
- Returns a text string that matches the error code in this QCborError object.
-
- Note: the string is not translated. Applications whose interface allow users
- to parse CBOR streams need to provide their own, translated strings.
-
- \sa QCborError::Code
- */
-QString QCborError::toString() const
-{
- switch (c) {
- case NoError:
- Q_STATIC_ASSERT(int(NoError) == int(CborNoError));
- return QString();
-
- case UnknownError:
- Q_STATIC_ASSERT(int(UnknownError) == int(CborUnknownError));
- return QStringLiteral("Unknown error");
- case AdvancePastEnd:
- Q_STATIC_ASSERT(int(AdvancePastEnd) == int(CborErrorAdvancePastEOF));
- return QStringLiteral("Read past end of buffer (more bytes needed)");
- case InputOutputError:
- Q_STATIC_ASSERT(int(InputOutputError) == int(CborErrorIO));
- return QStringLiteral("Input/Output error");
- case GarbageAtEnd:
- Q_STATIC_ASSERT(int(GarbageAtEnd) == int(CborErrorGarbageAtEnd));
- return QStringLiteral("Data found after the end of the stream");
- case EndOfFile:
- Q_STATIC_ASSERT(int(EndOfFile) == int(CborErrorUnexpectedEOF));
- return QStringLiteral("Unexpected end of input data (more bytes needed)");
- case UnexpectedBreak:
- Q_STATIC_ASSERT(int(UnexpectedBreak) == int(CborErrorUnexpectedBreak));
- return QStringLiteral("Invalid CBOR stream: unexpected 'break' byte");
- case UnknownType:
- Q_STATIC_ASSERT(int(UnknownType) == int(CborErrorUnknownType));
- return QStringLiteral("Invalid CBOR stream: unknown type");
- case IllegalType:
- Q_STATIC_ASSERT(int(IllegalType) == int(CborErrorIllegalType));
- return QStringLiteral("Invalid CBOR stream: illegal type found");
- case IllegalNumber:
- Q_STATIC_ASSERT(int(IllegalNumber) == int(CborErrorIllegalNumber));
- return QStringLiteral("Invalid CBOR stream: illegal number encoding (future extension)");
- case IllegalSimpleType:
- Q_STATIC_ASSERT(int(IllegalSimpleType) == int(CborErrorIllegalSimpleType));
- return QStringLiteral("Invalid CBOR stream: illegal simple type");
- case InvalidUtf8String:
- Q_STATIC_ASSERT(int(InvalidUtf8String) == int(CborErrorInvalidUtf8TextString));
- return QStringLiteral("Invalid CBOR stream: invalid UTF-8 text string");
- case DataTooLarge:
- Q_STATIC_ASSERT(int(DataTooLarge) == int(CborErrorDataTooLarge));
- return QStringLiteral("Internal limitation: data set too large");
- case NestingTooDeep:
- Q_STATIC_ASSERT(int(NestingTooDeep) == int(CborErrorNestingTooDeep));
- return QStringLiteral("Internal limitation: data nesting too deep");
- case UnsupportedType:
- Q_STATIC_ASSERT(int(UnsupportedType) == int(CborErrorUnsupportedType));
- return QStringLiteral("Internal limitation: unsupported type");
- }
-
- // get the error string from TinyCBOR
- CborError err = toCborError(*this);
- return QString::fromLatin1(cbor_error_string(err));
-}
-
-/*!
- \class QCborStreamWriter
- \inmodule QtCore
- \ingroup cbor
- \reentrant
- \since 5.12
-
- \brief The QCborStreamWriter class is a simple CBOR encoder operating on a
- one-way stream.
-
- This class can be used to quickly encode a stream of CBOR content directly
- to either a QByteArray or QIODevice. CBOR is the Concise Binary Object
- Representation, a very compact form of binary data encoding that is
- compatible with JSON. It was created by the IETF Constrained RESTful
- Environments (CoRE) WG, which has used it in many new RFCs. It is meant to
- be used alongside the \l{https://tools.ietf.org/html/rfc7252}{CoAP
- protocol}.
-
- QCborStreamWriter provides a StAX-like API, similar to that of
- \l{QXmlStreamWriter}. It is rather low-level and requires a bit of knowledge
- of CBOR encoding. For a simpler API, see \l{QCborValue} and especially the
- encoding function QCborValue::toCbor().
-
- The typical use of QCborStreamWriter is to create the object on the target
- QByteArray or QIODevice, then call one of the append() overloads with the
- desired type to be encoded. To create arrays and maps, QCborStreamWriter
- provides startArray() and startMap() overloads, which must be terminated by
- the corresponding endArray() and endMap() functions.
-
- The following example encodes the equivalent of this JSON content:
-
- \div{class="pre"}
- {
- "label": "journald",
- "autoDetect": false,
- "condition": "libs.journald",
- "output": [ "privateFeature" ]
- }
- \enddiv
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 1
-
- \section1 CBOR support
-
- QCborStreamWriter supports all CBOR features required to create canonical
- and strict streams. It implements almost all of the features specified in
- \l {https://tools.ietf.org/html/rfc7049}{RFC 7049}.
-
- The following table lists the CBOR features that QCborStreamWriter supports.
-
- \table
- \header \li Feature \li Support
- \row \li Unsigned numbers \li Yes (full range)
- \row \li Negative numbers \li Yes (full range)
- \row \li Byte strings \li Yes
- \row \li Text strings \li Yes
- \row \li Chunked strings \li No
- \row \li Tags \li Yes (arbitrary)
- \row \li Booleans \li Yes
- \row \li Null \li Yes
- \row \li Undefined \li Yes
- \row \li Arbitrary simple values \li Yes
- \row \li Half-precision float (16-bit) \li Yes
- \row \li Single-precision float (32-bit) \li Yes
- \row \li Double-precision float (64-bit) \li Yes
- \row \li Infinities and NaN floating point \li Yes
- \row \li Determinate-length arrays and maps \li Yes
- \row \li Indeterminate-length arrays and maps \li Yes
- \row \li Map key types other than strings and integers \li Yes (arbitrary)
- \endtable
-
- \section2 Canonical CBOR encoding
-
- Canonical CBOR encoding is defined by
- \l{https://tools.ietf.org/html/rfc7049#section-3.9}{Section 3.9 of RFC
- 7049}. Canonical encoding is not a requirement for Qt's CBOR decoding
- functionality, but it may be required for some protocols. In particular,
- protocols that require the ability to reproduce the same stream identically
- may require this.
-
- In order to be considered "canonical", a CBOR stream must meet the
- following requirements:
-
- \list
- \li Integers must be as small as possible. QCborStreamWriter always
- does this (no user action is required and it is not possible
- to write overlong integers).
- \li Array, map and string lengths must be as short as possible. As
- above, QCborStreamWriter automatically does this.
- \li Arrays, maps and strings must use explicit length. QCborStreamWriter
- always does this for strings; for arrays and maps, be sure to call
- startArray() and startMap() overloads with explicit length.
- \li Keys in every map must be sorted in ascending order. QCborStreamWriter
- offers no help in this item: the developer must ensure that before
- calling append() for the map pairs.
- \li Floating point values should be as small as possible. QCborStreamWriter
- will not convert floating point values; it is up to the developer
- to perform this check prior to calling append() (see those functions'
- examples).
- \endlist
-
- \section2 Strict CBOR mode
-
- Strict mode is defined by
- \l{https://tools.ietf.org/html/rfc7049#section-3.10}{Section 3.10 of RFC
- 7049}. As for Canonical encoding above, QCborStreamWriter makes it possible
- to create strict CBOR streams, but does not require them or validate that
- the output is so.
-
- \list
- \li Keys in a map must be unique. QCborStreamWriter performs no validation
- of map keys.
- \li Tags may be required to be paired only with the correct types,
- according to their specification. QCborStreamWriter performs no
- validation of tag usage.
- \li Text Strings must be properly-encoded UTF-8. QCborStreamWriter always
- writes proper UTF-8 for strings added with append(), but performs no
- validation for strings added with appendTextString().
- \endlist
-
- \section2 Invalid CBOR stream
-
- It is also possible to misuse QCborStreamWriter and produce invalid CBOR
- streams that will fail to be decoded by a receiver. The following actions
- will produce invalid streams:
-
- \list
- \li Append a tag and not append the corresponding tagged value
- (QCborStreamWriter produces no diagnostic).
- \li Append too many or too few items to an array or map with explicit
- length (endMap() and endArray() will return false and
- QCborStreamWriter will log with qWarning()).
- \endlist
-
- \sa QCborStreamReader, QCborValue, QXmlStreamWriter
- */
-
-class QCborStreamWriterPrivate
-{
-public:
- static Q_CONSTEXPR quint64 IndefiniteLength = (std::numeric_limits<quint64>::max)();
-
- QIODevice *device;
- CborEncoder encoder;
- QStack<CborEncoder> containerStack;
- bool deleteDevice = false;
-
- QCborStreamWriterPrivate(QIODevice *device)
- : device(device)
- {
- cbor_encoder_init_writer(&encoder, qt_cbor_encoder_write_callback, this);
- }
-
- ~QCborStreamWriterPrivate()
- {
- if (deleteDevice)
- delete device;
- }
-
- template <typename... Args> void executeAppend(CborError (*f)(CborEncoder *, Args...), Args... args)
- {
- f(&encoder, std::forward<Args>(args)...);
- }
-
- void createContainer(CborError (*f)(CborEncoder *, CborEncoder *, size_t), quint64 len = IndefiniteLength)
- {
- Q_STATIC_ASSERT(size_t(IndefiniteLength) == CborIndefiniteLength);
- if (sizeof(len) != sizeof(size_t) && len != IndefiniteLength) {
- if (Q_UNLIKELY(len >= CborIndefiniteLength)) {
- // TinyCBOR can't do this in 32-bit mode
- qWarning("QCborStreamWriter: container of size %llu is too big for a 32-bit build; "
- "will use indeterminate length instead", len);
- len = CborIndefiniteLength;
- }
- }
-
- containerStack.push(encoder);
- f(&containerStack.top(), &encoder, len);
- }
-
- bool closeContainer()
- {
- if (containerStack.isEmpty()) {
- qWarning("QCborStreamWriter: closing map or array that wasn't open");
- return false;
- }
-
- CborEncoder container = containerStack.pop();
- CborError err = cbor_encoder_close_container(&container, &encoder);
- encoder = container;
-
- if (Q_UNLIKELY(err)) {
- if (err == CborErrorTooFewItems)
- qWarning("QCborStreamWriter: not enough items added to array or map");
- else if (err == CborErrorTooManyItems)
- qWarning("QCborStreamWriter: too many items added to array or map");
- return false;
- }
-
- return true;
- }
-};
-
-static CborError qt_cbor_encoder_write_callback(void *self, const void *data, size_t len, CborEncoderAppendType)
-{
- auto that = static_cast<QCborStreamWriterPrivate *>(self);
- if (!that->device)
- return CborNoError;
- qint64 written = that->device->write(static_cast<const char *>(data), len);
- return (written == qsizetype(len) ? CborNoError : CborErrorIO);
-}
-
-/*!
- Creates a QCborStreamWriter object that will write the stream to \a device.
- The device must be opened before the first append() call is made. This
- constructor can be used with any class that derives from QIODevice, such as
- QFile, QProcess or QTcpSocket.
-
- QCborStreamWriter has no buffering, so every append() call will result in
- one or more calls to the device's \l {QIODevice::}{write()} method.
-
- The following example writes an empty map to a file:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 2
-
- QCborStreamWriter does not take ownership of \a device.
-
- \sa device(), setDevice()
- */
-QCborStreamWriter::QCborStreamWriter(QIODevice *device)
- : d(new QCborStreamWriterPrivate(device))
-{
-}
-
-/*!
- Creates a QCborStreamWriter object that will append the stream to \a data.
- All streaming is done immediately to the byte array, without the need for
- flushing any buffers.
-
- The following example writes a number to a byte array then returns
- it.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 3
-
- QCborStreamWriter does not take ownership of \a data.
- */
-QCborStreamWriter::QCborStreamWriter(QByteArray *data)
- : d(new QCborStreamWriterPrivate(new QBuffer(data)))
-{
- d->deleteDevice = true;
- d->device->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
-}
-
-/*!
- Destroys this QCborStreamWriter object and frees any resources associated.
-
- QCborStreamWriter does not perform error checking to see if all required
- items were written to the stream prior to the object being destroyed. It is
- the programmer's responsibility to ensure that it was done.
- */
-QCborStreamWriter::~QCborStreamWriter()
-{
-}
-
-/*!
- Replaces the device or byte array that this QCborStreamWriter object is
- writing to with \a device.
-
- \sa device()
- */
-void QCborStreamWriter::setDevice(QIODevice *device)
-{
- if (d->deleteDevice)
- delete d->device;
- d->device = device;
- d->deleteDevice = false;
-}
-
-/*!
- Returns the QIODevice that this QCborStreamWriter object is writing to. The
- device must have previously been set with either the constructor or with
- setDevice().
-
- If this object was created by writing to a QByteArray, this function will
- return an internal instance of QBuffer, which is owned by QCborStreamWriter.
-
- \sa setDevice()
- */
-QIODevice *QCborStreamWriter::device() const
-{
- return d->device;
-}
-
-/*!
- \overload
-
- Appends the 64-bit unsigned value \a u to the CBOR stream, creating a CBOR
- Unsigned Integer value. In the following example, we write the values 0,
- 2\sup{32} and \c UINT64_MAX:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 4
-
- \sa QCborStreamReader::isUnsignedInteger(), QCborStreamReader::toUnsignedInteger()
- */
-void QCborStreamWriter::append(quint64 u)
-{
- d->executeAppend(cbor_encode_uint, uint64_t(u));
-}
-
-/*!
- \overload
-
- Appends the 64-bit signed value \a i to the CBOR stream. This will create
- either a CBOR Unsigned Integer or CBOR NegativeInteger value based on the
- sign of the parameter. In the following example, we write the values 0, -1,
- 2\sup{32} and \c INT64_MAX:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 5
-
- \sa QCborStreamReader::isInteger(), QCborStreamReader::toInteger()
- */
-void QCborStreamWriter::append(qint64 i)
-{
- d->executeAppend(cbor_encode_int, int64_t(i));
-}
-
-/*!
- \overload
-
- Appends the 64-bit negative value \a n to the CBOR stream.
- QCborNegativeInteger is a 64-bit enum that holds the absolute value of the
- negative number we want to write. If n is zero, the value written will be
- equivalent to 2\sup{64} (that is, -18,446,744,073,709,551,616).
-
- In the following example, we write the values -1, -2\sup{32} and INT64_MIN:
- \snippet code/src_corelib_serialization_qcborstream.cpp 6
-
- Note how this function can be used to encode numbers that cannot fit a
- standard computer's 64-bit signed integer like \l qint64. That is, if \a n
- is larger than \c{std::numeric_limits<qint64>::max()} or is 0, this will
- represent a negative number smaller than
- \c{std::numeric_limits<qint64>::min()}.
-
- \sa QCborStreamReader::isNegativeInteger(), QCborStreamReader::toNegativeInteger()
- */
-void QCborStreamWriter::append(QCborNegativeInteger n)
-{
- d->executeAppend(cbor_encode_negative_int, uint64_t(n));
-}
-
-/*!
- \fn void QCborStreamWriter::append(const QByteArray &ba)
- \overload
-
- Appends the byte array \a ba to the stream, creating a CBOR Byte String
- value. QCborStreamWriter will attempt to write the entire string in one
- chunk.
-
- The following example will load and append the contents of a file to the
- stream:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 7
-
- As the example shows, unlike JSON, CBOR requires no escaping for binary
- content.
-
- \sa appendByteString(), QCborStreamReader::isByteArray(),
- QCborStreamReader::readByteArray()
- */
-
-/*!
- \overload
-
- Appends the text string \a str to the stream, creating a CBOR Text String
- value. QCborStreamWriter will attempt to write the entire string in one
- chunk.
-
- The following example appends a simple string to the stream:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 8
-
- \b{Performance note}: CBOR requires that all Text Strings be encoded in
- UTF-8, so this function will iterate over the characters in the string to
- determine whether the contents are US-ASCII or not. If the string is found
- to contain characters outside of US-ASCII, it will allocate memory and
- convert to UTF-8. If this check is unnecessary, use appendTextString()
- instead.
-
- \sa QCborStreamReader::isString(), QCborStreamReader::readString()
- */
-void QCborStreamWriter::append(QLatin1String str)
-{
- // We've got Latin-1 but CBOR wants UTF-8, so check if the string is the
- // common subset (US-ASCII).
- if (QtPrivate::isAscii(str)) {
- // it is plain US-ASCII
- appendTextString(str.latin1(), str.size());
- } else {
- // non-ASCII, so we need a pass-through UTF-16
- append(QString(str));
- }
-}
-
-/*!
- \overload
-
- Appends the text string \a str to the stream, creating a CBOR Text String
- value. QCborStreamWriter will attempt to write the entire string in one
- chunk.
-
- The following example writes an arbitrary QString to the stream:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 9
-
- \sa QCborStreamReader::isString(), QCborStreamReader::readString()
- */
-void QCborStreamWriter::append(QStringView str)
-{
- QByteArray utf8 = str.toUtf8();
- appendTextString(utf8.constData(), utf8.size());
-}
-
-/*!
- \overload
-
- Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All
- tags must be followed by another type which they provide meaning for.
-
- In the following example, we append a CBOR Tag 36 (Regular Expression) and a
- QRegularExpression's pattern to the stream:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 10
-
- \sa QCborStreamReader::isTag(), QCborStreamReader::toTag()
- */
-void QCborStreamWriter::append(QCborTag tag)
-{
- d->executeAppend(cbor_encode_tag, CborTag(tag));
-}
-
-/*!
- \fn void QCborStreamWriter::append(QCborKnownTags tag)
- \overload
-
- Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All
- tags must be followed by another type which they provide meaning for.
-
- In the following example, we append a CBOR Tag 1 (Unix \c time_t) and an
- integer representing the current time to the stream, obtained using the \c
- time() function:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 11
-
- \sa QCborStreamReader::isTag(), QCborStreamReader::toTag()
- */
-
-/*!
- \overload
-
- Appends the CBOR simple type \a st to the stream, creating a CBOR Simple
- Type value. In the following example, we write the simple type for Null as
- well as for type 32, which Qt has no support for.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 12
-
- \note Using Simple Types for which there is no specification can lead to
- validation errors by the remote receiver. In addition, simple type values 24
- through 31 (inclusive) are reserved and must not be used.
-
- \sa QCborStreamReader::isSimpleType(), QCborStreamReader::toSimpleType()
- */
-void QCborStreamWriter::append(QCborSimpleType st)
-{
- d->executeAppend(cbor_encode_simple_value, uint8_t(st));
-}
-
-/*!
- \overload
-
- Appends the floating point number \a f to the stream, creating a CBOR 16-bit
- Half-Precision Floating Point value. The following code can be used to convert
- a C++ \tt float to \c qfloat16 if there's no loss of precision and append it, or
- instead append the \tt float.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 13
-
- \sa QCborStreamReader::isFloat16(), QCborStreamReader::toFloat16()
- */
-void QCborStreamWriter::append(qfloat16 f)
-{
- d->executeAppend(cbor_encode_half_float, static_cast<const void *>(&f));
-}
-
-/*!
- \overload
-
- Appends the floating point number \a f to the stream, creating a CBOR 32-bit
- Single-Precision Floating Point value. The following code can be used to convert
- a C++ \tt double to \tt float if there's no loss of precision and append it, or
- instead append the \tt double.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 14
-
- \sa QCborStreamReader::isFloat(), QCborStreamReader::toFloat()
- */
-void QCborStreamWriter::append(float f)
-{
- d->executeAppend(cbor_encode_float, f);
-}
-
-/*!
- \overload
-
- Appends the floating point number \a d to the stream, creating a CBOR 64-bit
- Double-Precision Floating Point value. QCborStreamWriter always appends the
- number as-is, performing no check for whether the number is the canonical
- form for NaN, an infinite, whether it is denormal or if it could be written
- with a shorter format.
-
- The following code performs all those checks, except for the denormal one,
- which is expected to be taken into account by the system FPU or floating
- point emulation directly.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 15
-
- Determining if a double can be converted to an integral with no loss of
- precision is left as an exercise to the reader.
-
- \sa QCborStreamReader::isDouble(), QCborStreamReader::toDouble()
- */
-void QCborStreamWriter::append(double d)
-{
- this->d->executeAppend(cbor_encode_double, d);
-}
-
-/*!
- Appends \a len bytes of data starting from \a data to the stream, creating a
- CBOR Byte String value. QCborStreamWriter will attempt to write the entire
- string in one chunk.
-
- Unlike the QByteArray overload of append(), this function is not limited by
- QByteArray's size limits. However, note that neither
- QCborStreamReader::readByteArray() nor QCborValue support reading CBOR
- streams with byte arrays larger than 2 GB.
-
- \sa append(), appendTextString(),
- QCborStreamReader::isByteArray(), QCborStreamReader::readByteArray()
- */
-void QCborStreamWriter::appendByteString(const char *data, qsizetype len)
-{
- d->executeAppend(cbor_encode_byte_string, reinterpret_cast<const uint8_t *>(data), size_t(len));
-}
-
-/*!
- Appends \a len bytes of text starting from \a utf8 to the stream, creating a
- CBOR Text String value. QCborStreamWriter will attempt to write the entire
- string in one chunk.
-
- The string pointed to by \a utf8 is expected to be properly encoded UTF-8.
- QCborStreamWriter performs no validation that this is the case.
-
- Unlike the QLatin1String overload of append(), this function is not limited
- to 2 GB. However, note that neither QCborStreamReader::readString() nor
- QCborValue support reading CBOR streams with text strings larger than 2 GB.
-
- \sa append(QLatin1String), append(QStringView),
- QCborStreamReader::isString(), QCborStreamReader::readString()
- */
-void QCborStreamWriter::appendTextString(const char *utf8, qsizetype len)
-{
- d->executeAppend(cbor_encode_text_string, utf8, size_t(len));
-}
-
-/*!
- \fn void QCborStreamWriter::append(const char *str, qsizetype size)
- \overload
-
- Appends \a size bytes of text starting from \a str to the stream, creating a
- CBOR Text String value. QCborStreamWriter will attempt to write the entire
- string in one chunk. If \a size is -1, this function will write \c strlen(\a
- str) bytes.
-
- The string pointed to by \a str is expected to be properly encoded UTF-8.
- QCborStreamWriter performs no validation that this is the case.
-
- Unlike the QLatin1String overload of append(), this function is not limited
- to 2 GB. However, note that neither QCborStreamReader nor QCborValue support
- reading CBOR streams with text strings larger than 2 GB.
-
- \sa append(QLatin1String), append(QStringView),
- QCborStreamReader::isString(), QCborStreamReader::readString()
- */
-
-/*!
- \fn void QCborStreamWriter::append(bool b)
- \overload
-
- Appends the boolean value \a b to the stream, creating either a CBOR False
- value or a CBOR True value. This function is equivalent to (and implemented
- as):
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 16
-
- \sa appendNull(), appendUndefined(),
- QCborStreamReader::isBool(), QCborStreamReader::toBool()
- */
-
-/*!
- \fn void QCborStreamWriter::append(std::nullptr_t)
- \overload
-
- Appends a CBOR Null value to the stream. This function is equivalent to (and
- implemented as): The parameter is ignored.
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 17
-
- \sa appendNull(), append(QCborSimpleType), QCborStreamReader::isNull()
- */
-
-/*!
- \fn void QCborStreamWriter::appendNull()
-
- Appends a CBOR Null value to the stream. This function is equivalent to (and
- implemented as):
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 18
-
- \sa append(std::nullptr_t), append(QCborSimpleType), QCborStreamReader::isNull()
- */
-
-/*!
- \fn void QCborStreamWriter::appendUndefined()
-
- Appends a CBOR Undefined value to the stream. This function is equivalent to (and
- implemented as):
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 19
-
- \sa append(QCborSimpleType), QCborStreamReader::isUndefined()
- */
-
-/*!
- Starts a CBOR Array with indeterminate length in the CBOR stream. Each
- startArray() call must be paired with one endArray() call and the current
- CBOR element extends until the end of the array.
-
- The array created by this function has no explicit length. Instead, its
- length is implied by the elements contained in it. Note, however, that use
- of indeterminate-length arrays is not compliant with canonical CBOR encoding.
-
- The following example appends elements from the linked list of strings
- passed as input:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 20
-
- \sa startArray(quint64), endArray(), startMap(), QCborStreamReader::isArray(),
- QCborStreamReader::isLengthKnown()
- */
-void QCborStreamWriter::startArray()
-{
- d->createContainer(cbor_encoder_create_array);
-}
-
-/*!
- \overload
-
- Starts a CBOR Array with explicit length of \a count items in the CBOR
- stream. Each startArray call must be paired with one endArray() call and the
- current CBOR element extends until the end of the array.
-
- The array created by this function has an explicit length and therefore
- exactly \a count items must be added to the CBOR stream. Adding fewer or
- more items will result in failure during endArray() and the CBOR stream will
- be corrupt. However, explicit-length arrays are required by canonical CBOR
- encoding.
-
- The following example appends all strings found in the \l QStringList passed as input:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 21
-
- \b{Size limitations}: The parameter to this function is quint64, which would
- seem to allow up to 2\sup{64}-1 elements in the array. However, both
- QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{32}-2
- items on 32-bit systems and 2\sup{64}-2 items on 64-bit ones. Also note that
- QCborArray is currently limited to 2\sup{27} elements in any platform.
-
- \sa startArray(), endArray(), startMap(), QCborStreamReader::isArray(),
- QCborStreamReader::isLengthKnown()
- */
-void QCborStreamWriter::startArray(quint64 count)
-{
- d->createContainer(cbor_encoder_create_array, count);
-}
-
-/*!
- Terminates the array started by either overload of startArray() and returns
- true if the correct number of elements was added to the array. This function
- must be called for every startArray() used.
-
- A return of false indicates error in the application and an unrecoverable
- error in this stream. QCborStreamWriter also writes a warning using
- qWarning() if that happens.
-
- Calling this function when the current container is not an array is also an
- error, though QCborStreamWriter cannot currently detect this condition.
-
- \sa startArray(), startArray(quint64), endMap()
- */
-bool QCborStreamWriter::endArray()
-{
- return d->closeContainer();
-}
-
-/*!
- Starts a CBOR Map with indeterminate length in the CBOR stream. Each
- startMap() call must be paired with one endMap() call and the current CBOR
- element extends until the end of the map.
-
- The map created by this function has no explicit length. Instead, its length
- is implied by the elements contained in it. Note, however, that use of
- indeterminate-length maps is not compliant with canonical CBOR encoding
- (canonical encoding also requires keys to be unique and in sorted order).
-
- The following example appends elements from the linked list of int and
- string pairs passed as input:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 22
-
- \sa startMap(quint64), endMap(), startArray(), QCborStreamReader::isMap(),
- QCborStreamReader::isLengthKnown()
- */
-void QCborStreamWriter::startMap()
-{
- d->createContainer(cbor_encoder_create_map);
-}
-
-/*!
- \overload
-
- Starts a CBOR Map with explicit length of \a count items in the CBOR
- stream. Each startMap call must be paired with one endMap() call and the
- current CBOR element extends until the end of the map.
-
- The map created by this function has an explicit length and therefore
- exactly \a count pairs of items must be added to the CBOR stream. Adding
- fewer or more items will result in failure during endMap() and the CBOR
- stream will be corrupt. However, explicit-length map are required by
- canonical CBOR encoding.
-
- The following example appends all strings found in the \l QMap passed as input:
-
- \snippet code/src_corelib_serialization_qcborstream.cpp 23
-
- \b{Size limitations}: The parameter to this function is quint64, which would
- seem to allow up to 2\sup{64}-1 pairs in the map. However, both
- QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{31}-1
- items on 32-bit systems and 2\sup{63}-1 items on 64-bit ones. Also note that
- QCborMap is currently limited to 2\sup{26} elements in any platform.
-
- \sa startMap(), endMap(), startArray(), QCborStreamReader::isMap(),
- QCborStreamReader::isLengthKnown()
- */
-void QCborStreamWriter::startMap(quint64 count)
-{
- d->createContainer(cbor_encoder_create_map, count);
-}
-
-/*!
- Terminates the map started by either overload of startMap() and returns
- true if the correct number of elements was added to the array. This function
- must be called for every startMap() used.
-
- A return of false indicates error in the application and an unrecoverable
- error in this stream. QCborStreamWriter also writes a warning using
- qWarning() if that happens.
-
- Calling this function when the current container is not a map is also an
- error, though QCborStreamWriter cannot currently detect this condition.
-
- \sa startMap(), startMap(quint64), endArray()
- */
-bool QCborStreamWriter::endMap()
-{
- return d->closeContainer();
-}
-
-/*!
\class QCborStreamReader
\inmodule QtCore
\ingroup cbor
@@ -1868,7 +641,7 @@ public:
if (err != CborErrorUnexpectedEOF)
corrupt = true;
- lastError = fromCborError(err);
+ lastError = QCborError { QCborError::Code(int(err)) };
}
void updateBufferAfterString(qsizetype offset, qsizetype size)
@@ -2757,5 +1530,4 @@ QCborStreamReader::readStringChunk(char *ptr, qsizetype maxlen)
QT_END_NAMESPACE
-#include "moc_qcborcommon.cpp"
-#include "moc_qcborstream.cpp"
+#include "moc_qcborstreamreader.cpp"
diff --git a/src/corelib/serialization/qcborstreamreader.h b/src/corelib/serialization/qcborstreamreader.h
new file mode 100644
index 0000000000..6d5feccfcf
--- /dev/null
+++ b/src/corelib/serialization/qcborstreamreader.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QCBORSTREAMREADER_H
+#define QCBORSTREAMREADER_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qcborcommon.h>
+#include <QtCore/qfloat16.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringview.h>
+
+QT_REQUIRE_CONFIG(cborstreamreader);
+
+// See qcborcommon.h for why we check
+#if defined(QT_X11_DEFINES_FOUND)
+# undef True
+# undef False
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class QCborStreamReaderPrivate;
+class Q_CORE_EXPORT QCborStreamReader
+{
+ Q_GADGET
+public:
+ enum Type : quint8 {
+ UnsignedInteger = 0x00,
+ NegativeInteger = 0x20,
+ ByteString = 0x40,
+ ByteArray = ByteString,
+ TextString = 0x60,
+ String = TextString,
+ Array = 0x80,
+ Map = 0xa0,
+ Tag = 0xc0,
+ SimpleType = 0xe0,
+ HalfFloat = 0xf9,
+ Float16 = HalfFloat,
+ Float = 0xfa,
+ Double = 0xfb,
+
+ Invalid = 0xff
+ };
+ Q_ENUM(Type)
+
+ enum StringResultCode {
+ EndOfString = 0,
+ Ok = 1,
+ Error = -1
+ };
+ template <typename Container> struct StringResult {
+ Container data;
+ StringResultCode status = Error;
+ };
+ Q_ENUM(StringResultCode)
+
+ QCborStreamReader();
+ QCborStreamReader(const char *data, qsizetype len);
+ QCborStreamReader(const quint8 *data, qsizetype len);
+ explicit QCborStreamReader(const QByteArray &data);
+ explicit QCborStreamReader(QIODevice *device);
+ ~QCborStreamReader();
+ Q_DISABLE_COPY(QCborStreamReader)
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+ void addData(const QByteArray &data);
+ void addData(const char *data, qsizetype len);
+ void addData(const quint8 *data, qsizetype len)
+ { addData(reinterpret_cast<const char *>(data), len); }
+ void reparse();
+ void clear();
+ void reset();
+
+ QCborError lastError();
+
+ qint64 currentOffset() const;
+
+ bool isValid() const { return !isInvalid(); }
+
+ int containerDepth() const;
+ QCborStreamReader::Type parentContainerType() const;
+ bool hasNext() const noexcept Q_DECL_PURE_FUNCTION;
+ bool next(int maxRecursion = 10000);
+
+ Type type() const { return QCborStreamReader::Type(type_); }
+ bool isUnsignedInteger() const { return type() == UnsignedInteger; }
+ bool isNegativeInteger() const { return type() == NegativeInteger; }
+ bool isInteger() const { return quint8(type()) <= quint8(NegativeInteger); }
+ bool isByteArray() const { return type() == ByteArray; }
+ bool isString() const { return type() == String; }
+ bool isArray() const { return type() == Array; }
+ bool isMap() const { return type() == Map; }
+ bool isTag() const { return type() == Tag; }
+ bool isSimpleType() const { return type() == SimpleType; }
+ bool isFloat16() const { return type() == Float16; }
+ bool isFloat() const { return type() == Float; }
+ bool isDouble() const { return type() == Double; }
+ bool isInvalid() const { return type() == Invalid; }
+
+ bool isSimpleType(QCborSimpleType st) const { return isSimpleType() && toSimpleType() == st; }
+ bool isFalse() const { return isSimpleType(QCborSimpleType::False); }
+ bool isTrue() const { return isSimpleType(QCborSimpleType::True); }
+ bool isBool() const { return isFalse() || isTrue(); }
+ bool isNull() const { return isSimpleType(QCborSimpleType::Null); }
+ bool isUndefined() const { return isSimpleType(QCborSimpleType::Undefined); }
+
+ bool isLengthKnown() const noexcept Q_DECL_PURE_FUNCTION;
+ quint64 length() const;
+
+ bool isContainer() const { return isMap() || isArray(); }
+ bool enterContainer() { Q_ASSERT(isContainer()); return _enterContainer_helper(); }
+ bool leaveContainer();
+
+ StringResult<QString> readString() { Q_ASSERT(isString()); return _readString_helper(); }
+ StringResult<QByteArray> readByteArray(){ Q_ASSERT(isByteArray()); return _readByteArray_helper(); }
+ qsizetype currentStringChunkSize() const{ Q_ASSERT(isString() || isByteArray()); return _currentStringChunkSize(); }
+ StringResult<qsizetype> readStringChunk(char *ptr, qsizetype maxlen);
+
+ bool toBool() const { Q_ASSERT(isBool()); return value64 - int(QCborSimpleType::False); }
+ QCborTag toTag() const { Q_ASSERT(isTag()); return QCborTag(value64); }
+ quint64 toUnsignedInteger() const { Q_ASSERT(isUnsignedInteger()); return value64; }
+ QCborNegativeInteger toNegativeInteger() const { Q_ASSERT(isNegativeInteger()); return QCborNegativeInteger(value64 + 1); }
+ QCborSimpleType toSimpleType() const{ Q_ASSERT(isSimpleType()); return QCborSimpleType(value64); }
+ qfloat16 toFloat16() const { Q_ASSERT(isFloat16()); return _toFloatingPoint<qfloat16>(); }
+ float toFloat() const { Q_ASSERT(isFloat()); return _toFloatingPoint<float>(); }
+ double toDouble() const { Q_ASSERT(isDouble()); return _toFloatingPoint<double>(); }
+
+ qint64 toInteger() const
+ {
+ Q_ASSERT(isInteger());
+ qint64 v = qint64(value64);
+ if (isNegativeInteger())
+ return -v - 1;
+ return v;
+ }
+
+private:
+ void preparse();
+ bool _enterContainer_helper();
+ StringResult<QString> _readString_helper();
+ StringResult<QByteArray> _readByteArray_helper();
+ qsizetype _currentStringChunkSize() const;
+
+ template <typename FP> FP _toFloatingPoint() const noexcept
+ {
+ using UIntFP = typename QIntegerForSizeof<FP>::Unsigned;
+ UIntFP u = UIntFP(value64);
+ FP f;
+ memcpy(static_cast<void *>(&f), &u, sizeof(f));
+ return f;
+ }
+
+ friend QCborStreamReaderPrivate;
+ friend class QCborContainerPrivate;
+ quint64 value64;
+ QScopedPointer<QCborStreamReaderPrivate> d;
+ quint8 type_;
+ quint8 reserved[3] = {};
+};
+
+QT_END_NAMESPACE
+
+#if defined(QT_X11_DEFINES_FOUND)
+# define True 1
+# define False 0
+#endif
+
+#endif // QCBORSTREAMREADER_H
diff --git a/src/corelib/serialization/qcborstreamwriter.cpp b/src/corelib/serialization/qcborstreamwriter.cpp
new file mode 100644
index 0000000000..9d78785416
--- /dev/null
+++ b/src/corelib/serialization/qcborstreamwriter.cpp
@@ -0,0 +1,868 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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$
+**
+****************************************************************************/
+
+#include "qcborstreamwriter.h"
+
+#define CBOR_NO_PARSER_API
+#include <private/qcborcommon_p.h>
+
+#include <private/qnumeric_p.h>
+#include <qbuffer.h>
+#include <qdebug.h>
+#include <qstack.h>
+
+QT_BEGIN_NAMESPACE
+
+static CborError qt_cbor_encoder_write_callback(void *token, const void *data, size_t len, CborEncoderAppendType);
+#define CBOR_ENCODER_WRITER_CONTROL 1
+#define CBOR_ENCODER_WRITE_FUNCTION qt_cbor_encoder_write_callback
+#define CBOR_ENCODER_NO_CHECK_USER
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
+
+#include <cborencoder.c>
+
+QT_WARNING_POP
+
+// silence compilers that complain about this being a static function declared
+// but never defined
+static CborError Q_DECL_UNUSED cbor_encoder_close_container_checked(CborEncoder*, const CborEncoder*)
+{
+ Q_UNREACHABLE();
+ return CborErrorInternalError;
+}
+
+static CborError Q_DECL_UNUSED cbor_encode_float_as_half_float(CborEncoder *, float)
+{
+ Q_UNREACHABLE();
+ return CborErrorInternalError;
+}
+
+Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE);
+
+/*!
+ \class QCborStreamWriter
+ \inmodule QtCore
+ \ingroup cbor
+ \reentrant
+ \since 5.12
+
+ \brief The QCborStreamWriter class is a simple CBOR encoder operating on a
+ one-way stream.
+
+ This class can be used to quickly encode a stream of CBOR content directly
+ to either a QByteArray or QIODevice. CBOR is the Concise Binary Object
+ Representation, a very compact form of binary data encoding that is
+ compatible with JSON. It was created by the IETF Constrained RESTful
+ Environments (CoRE) WG, which has used it in many new RFCs. It is meant to
+ be used alongside the \l{https://tools.ietf.org/html/rfc7252}{CoAP
+ protocol}.
+
+ QCborStreamWriter provides a StAX-like API, similar to that of
+ \l{QXmlStreamWriter}. It is rather low-level and requires a bit of knowledge
+ of CBOR encoding. For a simpler API, see \l{QCborValue} and especially the
+ encoding function QCborValue::toCbor().
+
+ The typical use of QCborStreamWriter is to create the object on the target
+ QByteArray or QIODevice, then call one of the append() overloads with the
+ desired type to be encoded. To create arrays and maps, QCborStreamWriter
+ provides startArray() and startMap() overloads, which must be terminated by
+ the corresponding endArray() and endMap() functions.
+
+ The following example encodes the equivalent of this JSON content:
+
+ \div{class="pre"}
+ {
+ "label": "journald",
+ "autoDetect": false,
+ "condition": "libs.journald",
+ "output": [ "privateFeature" ]
+ }
+ \enddiv
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 1
+
+ \section1 CBOR support
+
+ QCborStreamWriter supports all CBOR features required to create canonical
+ and strict streams. It implements almost all of the features specified in
+ \l {https://tools.ietf.org/html/rfc7049}{RFC 7049}.
+
+ The following table lists the CBOR features that QCborStreamWriter supports.
+
+ \table
+ \header \li Feature \li Support
+ \row \li Unsigned numbers \li Yes (full range)
+ \row \li Negative numbers \li Yes (full range)
+ \row \li Byte strings \li Yes
+ \row \li Text strings \li Yes
+ \row \li Chunked strings \li No
+ \row \li Tags \li Yes (arbitrary)
+ \row \li Booleans \li Yes
+ \row \li Null \li Yes
+ \row \li Undefined \li Yes
+ \row \li Arbitrary simple values \li Yes
+ \row \li Half-precision float (16-bit) \li Yes
+ \row \li Single-precision float (32-bit) \li Yes
+ \row \li Double-precision float (64-bit) \li Yes
+ \row \li Infinities and NaN floating point \li Yes
+ \row \li Determinate-length arrays and maps \li Yes
+ \row \li Indeterminate-length arrays and maps \li Yes
+ \row \li Map key types other than strings and integers \li Yes (arbitrary)
+ \endtable
+
+ \section2 Canonical CBOR encoding
+
+ Canonical CBOR encoding is defined by
+ \l{https://tools.ietf.org/html/rfc7049#section-3.9}{Section 3.9 of RFC
+ 7049}. Canonical encoding is not a requirement for Qt's CBOR decoding
+ functionality, but it may be required for some protocols. In particular,
+ protocols that require the ability to reproduce the same stream identically
+ may require this.
+
+ In order to be considered "canonical", a CBOR stream must meet the
+ following requirements:
+
+ \list
+ \li Integers must be as small as possible. QCborStreamWriter always
+ does this (no user action is required and it is not possible
+ to write overlong integers).
+ \li Array, map and string lengths must be as short as possible. As
+ above, QCborStreamWriter automatically does this.
+ \li Arrays, maps and strings must use explicit length. QCborStreamWriter
+ always does this for strings; for arrays and maps, be sure to call
+ startArray() and startMap() overloads with explicit length.
+ \li Keys in every map must be sorted in ascending order. QCborStreamWriter
+ offers no help in this item: the developer must ensure that before
+ calling append() for the map pairs.
+ \li Floating point values should be as small as possible. QCborStreamWriter
+ will not convert floating point values; it is up to the developer
+ to perform this check prior to calling append() (see those functions'
+ examples).
+ \endlist
+
+ \section2 Strict CBOR mode
+
+ Strict mode is defined by
+ \l{https://tools.ietf.org/html/rfc7049#section-3.10}{Section 3.10 of RFC
+ 7049}. As for Canonical encoding above, QCborStreamWriter makes it possible
+ to create strict CBOR streams, but does not require them or validate that
+ the output is so.
+
+ \list
+ \li Keys in a map must be unique. QCborStreamWriter performs no validation
+ of map keys.
+ \li Tags may be required to be paired only with the correct types,
+ according to their specification. QCborStreamWriter performs no
+ validation of tag usage.
+ \li Text Strings must be properly-encoded UTF-8. QCborStreamWriter always
+ writes proper UTF-8 for strings added with append(), but performs no
+ validation for strings added with appendTextString().
+ \endlist
+
+ \section2 Invalid CBOR stream
+
+ It is also possible to misuse QCborStreamWriter and produce invalid CBOR
+ streams that will fail to be decoded by a receiver. The following actions
+ will produce invalid streams:
+
+ \list
+ \li Append a tag and not append the corresponding tagged value
+ (QCborStreamWriter produces no diagnostic).
+ \li Append too many or too few items to an array or map with explicit
+ length (endMap() and endArray() will return false and
+ QCborStreamWriter will log with qWarning()).
+ \endlist
+
+ \sa QCborStreamReader, QCborValue, QXmlStreamWriter
+ */
+
+class QCborStreamWriterPrivate
+{
+public:
+ static Q_CONSTEXPR quint64 IndefiniteLength = (std::numeric_limits<quint64>::max)();
+
+ QIODevice *device;
+ CborEncoder encoder;
+ QStack<CborEncoder> containerStack;
+ bool deleteDevice = false;
+
+ QCborStreamWriterPrivate(QIODevice *device)
+ : device(device)
+ {
+ cbor_encoder_init_writer(&encoder, qt_cbor_encoder_write_callback, this);
+ }
+
+ ~QCborStreamWriterPrivate()
+ {
+ if (deleteDevice)
+ delete device;
+ }
+
+ template <typename... Args> void executeAppend(CborError (*f)(CborEncoder *, Args...), Args... args)
+ {
+ f(&encoder, std::forward<Args>(args)...);
+ }
+
+ void createContainer(CborError (*f)(CborEncoder *, CborEncoder *, size_t), quint64 len = IndefiniteLength)
+ {
+ Q_STATIC_ASSERT(size_t(IndefiniteLength) == CborIndefiniteLength);
+ if (sizeof(len) != sizeof(size_t) && len != IndefiniteLength) {
+ if (Q_UNLIKELY(len >= CborIndefiniteLength)) {
+ // TinyCBOR can't do this in 32-bit mode
+ qWarning("QCborStreamWriter: container of size %llu is too big for a 32-bit build; "
+ "will use indeterminate length instead", len);
+ len = CborIndefiniteLength;
+ }
+ }
+
+ containerStack.push(encoder);
+ f(&containerStack.top(), &encoder, len);
+ }
+
+ bool closeContainer()
+ {
+ if (containerStack.isEmpty()) {
+ qWarning("QCborStreamWriter: closing map or array that wasn't open");
+ return false;
+ }
+
+ CborEncoder container = containerStack.pop();
+ CborError err = cbor_encoder_close_container(&container, &encoder);
+ encoder = container;
+
+ if (Q_UNLIKELY(err)) {
+ if (err == CborErrorTooFewItems)
+ qWarning("QCborStreamWriter: not enough items added to array or map");
+ else if (err == CborErrorTooManyItems)
+ qWarning("QCborStreamWriter: too many items added to array or map");
+ return false;
+ }
+
+ return true;
+ }
+};
+
+static CborError qt_cbor_encoder_write_callback(void *self, const void *data, size_t len, CborEncoderAppendType)
+{
+ auto that = static_cast<QCborStreamWriterPrivate *>(self);
+ if (!that->device)
+ return CborNoError;
+ qint64 written = that->device->write(static_cast<const char *>(data), len);
+ return (written == qsizetype(len) ? CborNoError : CborErrorIO);
+}
+
+/*!
+ Creates a QCborStreamWriter object that will write the stream to \a device.
+ The device must be opened before the first append() call is made. This
+ constructor can be used with any class that derives from QIODevice, such as
+ QFile, QProcess or QTcpSocket.
+
+ QCborStreamWriter has no buffering, so every append() call will result in
+ one or more calls to the device's \l {QIODevice::}{write()} method.
+
+ The following example writes an empty map to a file:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 2
+
+ QCborStreamWriter does not take ownership of \a device.
+
+ \sa device(), setDevice()
+ */
+QCborStreamWriter::QCborStreamWriter(QIODevice *device)
+ : d(new QCborStreamWriterPrivate(device))
+{
+}
+
+/*!
+ Creates a QCborStreamWriter object that will append the stream to \a data.
+ All streaming is done immediately to the byte array, without the need for
+ flushing any buffers.
+
+ The following example writes a number to a byte array then returns
+ it.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 3
+
+ QCborStreamWriter does not take ownership of \a data.
+ */
+QCborStreamWriter::QCborStreamWriter(QByteArray *data)
+ : d(new QCborStreamWriterPrivate(new QBuffer(data)))
+{
+ d->deleteDevice = true;
+ d->device->open(QIODevice::WriteOnly | QIODevice::Unbuffered);
+}
+
+/*!
+ Destroys this QCborStreamWriter object and frees any resources associated.
+
+ QCborStreamWriter does not perform error checking to see if all required
+ items were written to the stream prior to the object being destroyed. It is
+ the programmer's responsibility to ensure that it was done.
+ */
+QCborStreamWriter::~QCborStreamWriter()
+{
+}
+
+/*!
+ Replaces the device or byte array that this QCborStreamWriter object is
+ writing to with \a device.
+
+ \sa device()
+ */
+void QCborStreamWriter::setDevice(QIODevice *device)
+{
+ if (d->deleteDevice)
+ delete d->device;
+ d->device = device;
+ d->deleteDevice = false;
+}
+
+/*!
+ Returns the QIODevice that this QCborStreamWriter object is writing to. The
+ device must have previously been set with either the constructor or with
+ setDevice().
+
+ If this object was created by writing to a QByteArray, this function will
+ return an internal instance of QBuffer, which is owned by QCborStreamWriter.
+
+ \sa setDevice()
+ */
+QIODevice *QCborStreamWriter::device() const
+{
+ return d->device;
+}
+
+/*!
+ \overload
+
+ Appends the 64-bit unsigned value \a u to the CBOR stream, creating a CBOR
+ Unsigned Integer value. In the following example, we write the values 0,
+ 2\sup{32} and \c UINT64_MAX:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 4
+
+ \sa QCborStreamReader::isUnsignedInteger(), QCborStreamReader::toUnsignedInteger()
+ */
+void QCborStreamWriter::append(quint64 u)
+{
+ d->executeAppend(cbor_encode_uint, uint64_t(u));
+}
+
+/*!
+ \overload
+
+ Appends the 64-bit signed value \a i to the CBOR stream. This will create
+ either a CBOR Unsigned Integer or CBOR NegativeInteger value based on the
+ sign of the parameter. In the following example, we write the values 0, -1,
+ 2\sup{32} and \c INT64_MAX:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 5
+
+ \sa QCborStreamReader::isInteger(), QCborStreamReader::toInteger()
+ */
+void QCborStreamWriter::append(qint64 i)
+{
+ d->executeAppend(cbor_encode_int, int64_t(i));
+}
+
+/*!
+ \overload
+
+ Appends the 64-bit negative value \a n to the CBOR stream.
+ QCborNegativeInteger is a 64-bit enum that holds the absolute value of the
+ negative number we want to write. If n is zero, the value written will be
+ equivalent to 2\sup{64} (that is, -18,446,744,073,709,551,616).
+
+ In the following example, we write the values -1, -2\sup{32} and INT64_MIN:
+ \snippet code/src_corelib_serialization_qcborstream.cpp 6
+
+ Note how this function can be used to encode numbers that cannot fit a
+ standard computer's 64-bit signed integer like \l qint64. That is, if \a n
+ is larger than \c{std::numeric_limits<qint64>::max()} or is 0, this will
+ represent a negative number smaller than
+ \c{std::numeric_limits<qint64>::min()}.
+
+ \sa QCborStreamReader::isNegativeInteger(), QCborStreamReader::toNegativeInteger()
+ */
+void QCborStreamWriter::append(QCborNegativeInteger n)
+{
+ d->executeAppend(cbor_encode_negative_int, uint64_t(n));
+}
+
+/*!
+ \fn void QCborStreamWriter::append(const QByteArray &ba)
+ \overload
+
+ Appends the byte array \a ba to the stream, creating a CBOR Byte String
+ value. QCborStreamWriter will attempt to write the entire string in one
+ chunk.
+
+ The following example will load and append the contents of a file to the
+ stream:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 7
+
+ As the example shows, unlike JSON, CBOR requires no escaping for binary
+ content.
+
+ \sa appendByteString(), QCborStreamReader::isByteArray(),
+ QCborStreamReader::readByteArray()
+ */
+
+/*!
+ \overload
+
+ Appends the text string \a str to the stream, creating a CBOR Text String
+ value. QCborStreamWriter will attempt to write the entire string in one
+ chunk.
+
+ The following example appends a simple string to the stream:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 8
+
+ \b{Performance note}: CBOR requires that all Text Strings be encoded in
+ UTF-8, so this function will iterate over the characters in the string to
+ determine whether the contents are US-ASCII or not. If the string is found
+ to contain characters outside of US-ASCII, it will allocate memory and
+ convert to UTF-8. If this check is unnecessary, use appendTextString()
+ instead.
+
+ \sa QCborStreamReader::isString(), QCborStreamReader::readString()
+ */
+void QCborStreamWriter::append(QLatin1String str)
+{
+ // We've got Latin-1 but CBOR wants UTF-8, so check if the string is the
+ // common subset (US-ASCII).
+ if (QtPrivate::isAscii(str)) {
+ // it is plain US-ASCII
+ appendTextString(str.latin1(), str.size());
+ } else {
+ // non-ASCII, so we need a pass-through UTF-16
+ append(QString(str));
+ }
+}
+
+/*!
+ \overload
+
+ Appends the text string \a str to the stream, creating a CBOR Text String
+ value. QCborStreamWriter will attempt to write the entire string in one
+ chunk.
+
+ The following example writes an arbitrary QString to the stream:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 9
+
+ \sa QCborStreamReader::isString(), QCborStreamReader::readString()
+ */
+void QCborStreamWriter::append(QStringView str)
+{
+ QByteArray utf8 = str.toUtf8();
+ appendTextString(utf8.constData(), utf8.size());
+}
+
+/*!
+ \overload
+
+ Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All
+ tags must be followed by another type which they provide meaning for.
+
+ In the following example, we append a CBOR Tag 36 (Regular Expression) and a
+ QRegularExpression's pattern to the stream:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 10
+
+ \sa QCborStreamReader::isTag(), QCborStreamReader::toTag()
+ */
+void QCborStreamWriter::append(QCborTag tag)
+{
+ d->executeAppend(cbor_encode_tag, CborTag(tag));
+}
+
+/*!
+ \fn void QCborStreamWriter::append(QCborKnownTags tag)
+ \overload
+
+ Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All
+ tags must be followed by another type which they provide meaning for.
+
+ In the following example, we append a CBOR Tag 1 (Unix \c time_t) and an
+ integer representing the current time to the stream, obtained using the \c
+ time() function:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 11
+
+ \sa QCborStreamReader::isTag(), QCborStreamReader::toTag()
+ */
+
+/*!
+ \overload
+
+ Appends the CBOR simple type \a st to the stream, creating a CBOR Simple
+ Type value. In the following example, we write the simple type for Null as
+ well as for type 32, which Qt has no support for.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 12
+
+ \note Using Simple Types for which there is no specification can lead to
+ validation errors by the remote receiver. In addition, simple type values 24
+ through 31 (inclusive) are reserved and must not be used.
+
+ \sa QCborStreamReader::isSimpleType(), QCborStreamReader::toSimpleType()
+ */
+void QCborStreamWriter::append(QCborSimpleType st)
+{
+ d->executeAppend(cbor_encode_simple_value, uint8_t(st));
+}
+
+#ifndef QT_BOOTSTRAPPED
+/*!
+ \overload
+
+ Appends the floating point number \a f to the stream, creating a CBOR 16-bit
+ Half-Precision Floating Point value. The following code can be used to convert
+ a C++ \tt float to \c qfloat16 if there's no loss of precision and append it, or
+ instead append the \tt float.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 13
+
+ \sa QCborStreamReader::isFloat16(), QCborStreamReader::toFloat16()
+ */
+void QCborStreamWriter::append(qfloat16 f)
+{
+ d->executeAppend(cbor_encode_half_float, static_cast<const void *>(&f));
+}
+#endif // QT_BOOTSTRAPPED
+
+/*!
+ \overload
+
+ Appends the floating point number \a f to the stream, creating a CBOR 32-bit
+ Single-Precision Floating Point value. The following code can be used to convert
+ a C++ \tt double to \tt float if there's no loss of precision and append it, or
+ instead append the \tt double.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 14
+
+ \sa QCborStreamReader::isFloat(), QCborStreamReader::toFloat()
+ */
+void QCborStreamWriter::append(float f)
+{
+ d->executeAppend(cbor_encode_float, f);
+}
+
+/*!
+ \overload
+
+ Appends the floating point number \a d to the stream, creating a CBOR 64-bit
+ Double-Precision Floating Point value. QCborStreamWriter always appends the
+ number as-is, performing no check for whether the number is the canonical
+ form for NaN, an infinite, whether it is denormal or if it could be written
+ with a shorter format.
+
+ The following code performs all those checks, except for the denormal one,
+ which is expected to be taken into account by the system FPU or floating
+ point emulation directly.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 15
+
+ Determining if a double can be converted to an integral with no loss of
+ precision is left as an exercise to the reader.
+
+ \sa QCborStreamReader::isDouble(), QCborStreamReader::toDouble()
+ */
+void QCborStreamWriter::append(double d)
+{
+ this->d->executeAppend(cbor_encode_double, d);
+}
+
+/*!
+ Appends \a len bytes of data starting from \a data to the stream, creating a
+ CBOR Byte String value. QCborStreamWriter will attempt to write the entire
+ string in one chunk.
+
+ Unlike the QByteArray overload of append(), this function is not limited by
+ QByteArray's size limits. However, note that neither
+ QCborStreamReader::readByteArray() nor QCborValue support reading CBOR
+ streams with byte arrays larger than 2 GB.
+
+ \sa append(), appendTextString(),
+ QCborStreamReader::isByteArray(), QCborStreamReader::readByteArray()
+ */
+void QCborStreamWriter::appendByteString(const char *data, qsizetype len)
+{
+ d->executeAppend(cbor_encode_byte_string, reinterpret_cast<const uint8_t *>(data), size_t(len));
+}
+
+/*!
+ Appends \a len bytes of text starting from \a utf8 to the stream, creating a
+ CBOR Text String value. QCborStreamWriter will attempt to write the entire
+ string in one chunk.
+
+ The string pointed to by \a utf8 is expected to be properly encoded UTF-8.
+ QCborStreamWriter performs no validation that this is the case.
+
+ Unlike the QLatin1String overload of append(), this function is not limited
+ to 2 GB. However, note that neither QCborStreamReader::readString() nor
+ QCborValue support reading CBOR streams with text strings larger than 2 GB.
+
+ \sa append(QLatin1String), append(QStringView),
+ QCborStreamReader::isString(), QCborStreamReader::readString()
+ */
+void QCborStreamWriter::appendTextString(const char *utf8, qsizetype len)
+{
+ d->executeAppend(cbor_encode_text_string, utf8, size_t(len));
+}
+
+/*!
+ \fn void QCborStreamWriter::append(const char *str, qsizetype size)
+ \overload
+
+ Appends \a size bytes of text starting from \a str to the stream, creating a
+ CBOR Text String value. QCborStreamWriter will attempt to write the entire
+ string in one chunk. If \a size is -1, this function will write \c strlen(\a
+ str) bytes.
+
+ The string pointed to by \a str is expected to be properly encoded UTF-8.
+ QCborStreamWriter performs no validation that this is the case.
+
+ Unlike the QLatin1String overload of append(), this function is not limited
+ to 2 GB. However, note that neither QCborStreamReader nor QCborValue support
+ reading CBOR streams with text strings larger than 2 GB.
+
+ \sa append(QLatin1String), append(QStringView),
+ QCborStreamReader::isString(), QCborStreamReader::readString()
+ */
+
+/*!
+ \fn void QCborStreamWriter::append(bool b)
+ \overload
+
+ Appends the boolean value \a b to the stream, creating either a CBOR False
+ value or a CBOR True value. This function is equivalent to (and implemented
+ as):
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 16
+
+ \sa appendNull(), appendUndefined(),
+ QCborStreamReader::isBool(), QCborStreamReader::toBool()
+ */
+
+/*!
+ \fn void QCborStreamWriter::append(std::nullptr_t)
+ \overload
+
+ Appends a CBOR Null value to the stream. This function is equivalent to (and
+ implemented as): The parameter is ignored.
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 17
+
+ \sa appendNull(), append(QCborSimpleType), QCborStreamReader::isNull()
+ */
+
+/*!
+ \fn void QCborStreamWriter::appendNull()
+
+ Appends a CBOR Null value to the stream. This function is equivalent to (and
+ implemented as):
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 18
+
+ \sa append(std::nullptr_t), append(QCborSimpleType), QCborStreamReader::isNull()
+ */
+
+/*!
+ \fn void QCborStreamWriter::appendUndefined()
+
+ Appends a CBOR Undefined value to the stream. This function is equivalent to (and
+ implemented as):
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 19
+
+ \sa append(QCborSimpleType), QCborStreamReader::isUndefined()
+ */
+
+/*!
+ Starts a CBOR Array with indeterminate length in the CBOR stream. Each
+ startArray() call must be paired with one endArray() call and the current
+ CBOR element extends until the end of the array.
+
+ The array created by this function has no explicit length. Instead, its
+ length is implied by the elements contained in it. Note, however, that use
+ of indeterminate-length arrays is not compliant with canonical CBOR encoding.
+
+ The following example appends elements from the linked list of strings
+ passed as input:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 20
+
+ \sa startArray(quint64), endArray(), startMap(), QCborStreamReader::isArray(),
+ QCborStreamReader::isLengthKnown()
+ */
+void QCborStreamWriter::startArray()
+{
+ d->createContainer(cbor_encoder_create_array);
+}
+
+/*!
+ \overload
+
+ Starts a CBOR Array with explicit length of \a count items in the CBOR
+ stream. Each startArray call must be paired with one endArray() call and the
+ current CBOR element extends until the end of the array.
+
+ The array created by this function has an explicit length and therefore
+ exactly \a count items must be added to the CBOR stream. Adding fewer or
+ more items will result in failure during endArray() and the CBOR stream will
+ be corrupt. However, explicit-length arrays are required by canonical CBOR
+ encoding.
+
+ The following example appends all strings found in the \l QStringList passed as input:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 21
+
+ \b{Size limitations}: The parameter to this function is quint64, which would
+ seem to allow up to 2\sup{64}-1 elements in the array. However, both
+ QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{32}-2
+ items on 32-bit systems and 2\sup{64}-2 items on 64-bit ones. Also note that
+ QCborArray is currently limited to 2\sup{27} elements in any platform.
+
+ \sa startArray(), endArray(), startMap(), QCborStreamReader::isArray(),
+ QCborStreamReader::isLengthKnown()
+ */
+void QCborStreamWriter::startArray(quint64 count)
+{
+ d->createContainer(cbor_encoder_create_array, count);
+}
+
+/*!
+ Terminates the array started by either overload of startArray() and returns
+ true if the correct number of elements was added to the array. This function
+ must be called for every startArray() used.
+
+ A return of false indicates error in the application and an unrecoverable
+ error in this stream. QCborStreamWriter also writes a warning using
+ qWarning() if that happens.
+
+ Calling this function when the current container is not an array is also an
+ error, though QCborStreamWriter cannot currently detect this condition.
+
+ \sa startArray(), startArray(quint64), endMap()
+ */
+bool QCborStreamWriter::endArray()
+{
+ return d->closeContainer();
+}
+
+/*!
+ Starts a CBOR Map with indeterminate length in the CBOR stream. Each
+ startMap() call must be paired with one endMap() call and the current CBOR
+ element extends until the end of the map.
+
+ The map created by this function has no explicit length. Instead, its length
+ is implied by the elements contained in it. Note, however, that use of
+ indeterminate-length maps is not compliant with canonical CBOR encoding
+ (canonical encoding also requires keys to be unique and in sorted order).
+
+ The following example appends elements from the linked list of int and
+ string pairs passed as input:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 22
+
+ \sa startMap(quint64), endMap(), startArray(), QCborStreamReader::isMap(),
+ QCborStreamReader::isLengthKnown()
+ */
+void QCborStreamWriter::startMap()
+{
+ d->createContainer(cbor_encoder_create_map);
+}
+
+/*!
+ \overload
+
+ Starts a CBOR Map with explicit length of \a count items in the CBOR
+ stream. Each startMap call must be paired with one endMap() call and the
+ current CBOR element extends until the end of the map.
+
+ The map created by this function has an explicit length and therefore
+ exactly \a count pairs of items must be added to the CBOR stream. Adding
+ fewer or more items will result in failure during endMap() and the CBOR
+ stream will be corrupt. However, explicit-length map are required by
+ canonical CBOR encoding.
+
+ The following example appends all strings found in the \l QMap passed as input:
+
+ \snippet code/src_corelib_serialization_qcborstream.cpp 23
+
+ \b{Size limitations}: The parameter to this function is quint64, which would
+ seem to allow up to 2\sup{64}-1 pairs in the map. However, both
+ QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{31}-1
+ items on 32-bit systems and 2\sup{63}-1 items on 64-bit ones. Also note that
+ QCborMap is currently limited to 2\sup{26} elements in any platform.
+
+ \sa startMap(), endMap(), startArray(), QCborStreamReader::isMap(),
+ QCborStreamReader::isLengthKnown()
+ */
+void QCborStreamWriter::startMap(quint64 count)
+{
+ d->createContainer(cbor_encoder_create_map, count);
+}
+
+/*!
+ Terminates the map started by either overload of startMap() and returns
+ true if the correct number of elements was added to the array. This function
+ must be called for every startMap() used.
+
+ A return of false indicates error in the application and an unrecoverable
+ error in this stream. QCborStreamWriter also writes a warning using
+ qWarning() if that happens.
+
+ Calling this function when the current container is not a map is also an
+ error, though QCborStreamWriter cannot currently detect this condition.
+
+ \sa startMap(), startMap(quint64), endArray()
+ */
+bool QCborStreamWriter::endMap()
+{
+ return d->closeContainer();
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/serialization/qcborstreamwriter.h b/src/corelib/serialization/qcborstreamwriter.h
new file mode 100644
index 0000000000..f8c94ceb93
--- /dev/null
+++ b/src/corelib/serialization/qcborstreamwriter.h
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 QCBORSTREAMWRITER_H
+#define QCBORSTREAMWRITER_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qcborcommon.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringview.h>
+#ifndef QT_BOOTSTRAPPED
+#include <QtCore/qfloat16.h>
+#endif
+
+QT_REQUIRE_CONFIG(cborstreamwriter);
+
+// See qcborcommon.h for why we check
+#if defined(QT_X11_DEFINES_FOUND)
+# undef True
+# undef False
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QIODevice;
+
+class QCborStreamWriterPrivate;
+class Q_CORE_EXPORT QCborStreamWriter
+{
+public:
+ explicit QCborStreamWriter(QIODevice *device);
+ explicit QCborStreamWriter(QByteArray *data);
+ ~QCborStreamWriter();
+ Q_DISABLE_COPY(QCborStreamWriter)
+
+ void setDevice(QIODevice *device);
+ QIODevice *device() const;
+
+ void append(quint64 u);
+ void append(qint64 i);
+ void append(QCborNegativeInteger n);
+ void append(const QByteArray &ba) { appendByteString(ba.constData(), ba.size()); }
+ void append(QLatin1String str);
+ void append(QStringView str);
+ void append(QCborTag tag);
+ void append(QCborKnownTags tag) { append(QCborTag(tag)); }
+ void append(QCborSimpleType st);
+ void append(std::nullptr_t) { append(QCborSimpleType::Null); }
+#ifndef QT_BOOTSTRAPPED
+ void append(qfloat16 f);
+#endif
+ void append(float f);
+ void append(double d);
+
+ void appendByteString(const char *data, qsizetype len);
+ void appendTextString(const char *utf8, qsizetype len);
+
+ // convenience
+ void append(bool b) { append(b ? QCborSimpleType::True : QCborSimpleType::False); }
+ void appendNull() { append(QCborSimpleType::Null); }
+ void appendUndefined() { append(QCborSimpleType::Undefined); }
+
+#ifndef Q_QDOC
+ // overloads to make normal code not complain
+ void append(int i) { append(qint64(i)); }
+ void append(uint u) { append(quint64(u)); }
+#endif
+#ifndef QT_NO_CAST_FROM_ASCII
+ void append(const char *str, qsizetype size = -1)
+ { appendTextString(str, (str && size == -1) ? int(strlen(str)) : size); }
+#endif
+
+ void startArray();
+ void startArray(quint64 count);
+ bool endArray();
+ void startMap();
+ void startMap(quint64 count);
+ bool endMap();
+
+ // no API for encoding chunked strings
+
+private:
+ QScopedPointer<QCborStreamWriterPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#if defined(QT_X11_DEFINES_FOUND)
+# define True 1
+# define False 0
+#endif
+
+#endif // QCBORSTREAMWRITER_H
diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp
index 4052bfa22e..db2840704c 100644
--- a/src/corelib/serialization/qcborvalue.cpp
+++ b/src/corelib/serialization/qcborvalue.cpp
@@ -42,7 +42,14 @@
#include "qdatastream.h"
#include "qcborarray.h"
#include "qcbormap.h"
-#include "qcborstream.h"
+
+#if QT_CONFIG(cborstreamreader)
+#include "qcborstreamreader.h"
+#endif
+
+#if QT_CONFIG(cborstreamwriter)
+#include "qcborstreamwriter.h"
+#endif
#include <qendian.h>
#include <qlocale.h>
@@ -795,6 +802,7 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d)
break;
}
+#ifndef QT_BOOTSTRAPPED
case qint64(QCborKnownTags::Url):
if (e.type == QCborValue::String) {
if (b) {
@@ -808,13 +816,16 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d)
return QCborValue::Url;
}
break;
+#endif // QT_BOOTSTRAPPED
+#if QT_CONFIG(regularexpression)
case quint64(QCborKnownTags::RegularExpression):
if (e.type == QCborValue::String) {
// no normalization is necessary
return QCborValue::RegularExpression;
}
break;
+#endif // QT_CONFIG(regularexpression)
case qint64(QCborKnownTags::Uuid):
if (e.type == QCborValue::ByteArray) {
@@ -833,15 +844,20 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d)
return QCborValue::Tag;
}
+#if QT_CONFIG(cborstreamreader)
// in qcborstream.cpp
extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error);
+#endif
+#if QT_CONFIG(cborstreamwriter)
static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::EncodingOptions opt)
{
if (qt_is_nan(d)) {
- if (opt & QCborValue::UseFloat16) {
+ if (opt & QCborValue::UseFloat) {
+#ifndef QT_BOOTSTRAPPED
if ((opt & QCborValue::UseFloat16) == QCborValue::UseFloat16)
return writer.append(std::numeric_limits<qfloat16>::quiet_NaN());
+#endif
return writer.append(std::numeric_limits<float>::quiet_NaN());
}
return writer.append(qt_qnan());
@@ -858,15 +874,17 @@ static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::E
}
}
- if (opt & QCborValue::UseFloat16) {
+ if (opt & QCborValue::UseFloat) {
float f = float(d);
if (f == d) {
// no data loss, we could use float
+#ifndef QT_BOOTSTRAPPED
if ((opt & QCborValue::UseFloat16) == QCborValue::UseFloat16) {
qfloat16 f16 = f;
if (f16 == f)
return writer.append(f16);
}
+#endif
return writer.append(f);
}
@@ -874,6 +892,7 @@ static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::E
writer.append(d);
}
+#endif // QT_CONFIG(cborstreamwriter)
static inline int typeOrder(Element e1, Element e2)
{
@@ -1296,6 +1315,7 @@ int QCborMap::compare(const QCborMap &other) const noexcept
return compareContainer(d.data(), other.d.data());
}
+#if QT_CONFIG(cborstreamwriter)
static void encodeToCbor(QCborStreamWriter &writer, const QCborContainerPrivate *d, qsizetype idx,
QCborValue::EncodingOptions opt)
{
@@ -1383,7 +1403,9 @@ static void encodeToCbor(QCborStreamWriter &writer, const QCborContainerPrivate
qWarning("QCborValue: found unknown type 0x%x", e.type);
}
}
+#endif // QT_CONFIG(cborstreamwriter)
+#if QT_CONFIG(cborstreamreader)
static inline double integerOutOfRange(const QCborStreamReader &reader)
{
Q_ASSERT(reader.isInteger());
@@ -1640,6 +1662,7 @@ void QCborContainerPrivate::decodeFromCbor(QCborStreamReader &reader)
if (reader.lastError() == QCborError::NoError)
reader.leaveContainer();
}
+#endif // QT_CONFIG(cborstreamreader)
/*!
Creates a QCborValue with byte array value \a ba. The value can later be
@@ -1774,6 +1797,7 @@ QCborValue::QCborValue(const QDateTime &dt)
container->elements[1].type = String;
}
+#ifndef QT_BOOTSTRAPPED
/*!
Creates a QCborValue object of the URL extended type and containing the
value represented by \a url. The value can later be retrieved using toUrl().
@@ -1790,6 +1814,7 @@ QCborValue::QCborValue(const QUrl &url)
t = Url;
container->elements[1].type = String;
}
+#endif
#if QT_CONFIG(regularexpression)
/*!
@@ -1943,6 +1968,7 @@ QDateTime QCborValue::toDateTime(const QDateTime &defaultValue) const
return QDateTime::fromString(byteData->asLatin1(), Qt::ISODateWithMs);
}
+#ifndef QT_BOOTSTRAPPED
/*!
Returns the URL value stored in this QCborValue, if it is of the URL
extended type. Otherwise, it returns \a defaultValue.
@@ -1963,6 +1989,7 @@ QUrl QCborValue::toUrl(const QUrl &defaultValue) const
return QUrl::fromEncoded(byteData->asByteArrayView());
}
+#endif
#if QT_CONFIG(regularexpression)
/*!
@@ -2335,6 +2362,7 @@ QCborValueRef QCborValue::operator[](qint64 key)
return { container, index };
}
+#if QT_CONFIG(cborstreamreader)
/*!
Decodes one item from the CBOR stream found in \a reader and returns the
equivalent representation. This function is recursive: if the item is a map
@@ -2449,7 +2477,9 @@ QCborValue QCborValue::fromCbor(const QByteArray &ba, QCborParserError *error)
overload of this function that accepts a QByteArray, also passing \a error,
if provided.
*/
+#endif // QT_CONFIG(cborstreamreader)
+#if QT_CONFIG(cborstreamwriter)
/*!
Encodes this QCborValue object to its CBOR representation, using the
options specified in \a opt, and return the byte array containing that
@@ -2572,6 +2602,7 @@ void QCborValueRef::toCbor(QCborStreamWriter &writer, QCborValue::EncodingOption
{
concrete().toCbor(writer, opt);
}
+#endif // QT_CONFIG(cborstreamwriter)
void QCborValueRef::assign(QCborValueRef that, const QCborValue &other)
{
@@ -2891,8 +2922,10 @@ uint qHash(const QCborValue &value, uint seed)
return qHash(value.toDouble(), seed);
case QCborValue::DateTime:
return qHash(value.toDateTime(), seed);
+#ifndef QT_BOOTSTRAPPED
case QCborValue::Url:
return qHash(value.toUrl(), seed);
+#endif
#if QT_CONFIG(regularexpression)
case QCborValue::RegularExpression:
return qHash(value.toRegularExpression(), seed);
@@ -2945,8 +2978,10 @@ static QDebug debugContents(QDebug &dbg, const QCborValue &v)
}
case QCborValue::DateTime:
return dbg << v.toDateTime();
+#ifndef QT_BOOTSTRAPPED
case QCborValue::Url:
return dbg << v.toUrl();
+#endif
#if QT_CONFIG(regularexpression)
case QCborValue::RegularExpression:
return dbg << v.toRegularExpression();
@@ -2968,6 +3003,112 @@ QDebug operator<<(QDebug dbg, const QCborValue &v)
dbg.nospace() << "QCborValue(";
return debugContents(dbg, v) << ')';
}
+
+Q_CORE_EXPORT const char *qt_cbor_simpletype_id(QCborSimpleType st)
+{
+ switch (st) {
+ case QCborSimpleType::False:
+ return "False";
+ case QCborSimpleType::True:
+ return "True";
+ case QCborSimpleType::Null:
+ return "Null";
+ case QCborSimpleType::Undefined:
+ return "Undefined";
+ }
+ return nullptr;
+}
+
+QDebug operator<<(QDebug dbg, QCborSimpleType st)
+{
+ QDebugStateSaver saver(dbg);
+ const char *id = qt_cbor_simpletype_id(st);
+ if (id)
+ return dbg.nospace() << "QCborSimpleType::" << id;
+
+ return dbg.nospace() << "QCborSimpleType(" << uint(st) << ')';
+}
+
+Q_CORE_EXPORT const char *qt_cbor_tag_id(QCborTag tag)
+{
+ // Casting to QCborKnownTags's underlying type will make the comparison
+ // below fail if the tag value is out of range.
+ auto n = std::underlying_type<QCborKnownTags>::type(tag);
+ if (QCborTag(n) == tag) {
+ switch (QCborKnownTags(n)) {
+ case QCborKnownTags::DateTimeString:
+ return "DateTimeString";
+ case QCborKnownTags::UnixTime_t:
+ return "UnixTime_t";
+ case QCborKnownTags::PositiveBignum:
+ return "PositiveBignum";
+ case QCborKnownTags::NegativeBignum:
+ return "NegativeBignum";
+ case QCborKnownTags::Decimal:
+ return "Decimal";
+ case QCborKnownTags::Bigfloat:
+ return "Bigfloat";
+ case QCborKnownTags::COSE_Encrypt0:
+ return "COSE_Encrypt0";
+ case QCborKnownTags::COSE_Mac0:
+ return "COSE_Mac0";
+ case QCborKnownTags::COSE_Sign1:
+ return "COSE_Sign1";
+ case QCborKnownTags::ExpectedBase64url:
+ return "ExpectedBase64url";
+ case QCborKnownTags::ExpectedBase64:
+ return "ExpectedBase64";
+ case QCborKnownTags::ExpectedBase16:
+ return "ExpectedBase16";
+ case QCborKnownTags::EncodedCbor:
+ return "EncodedCbor";
+ case QCborKnownTags::Url:
+ return "Url";
+ case QCborKnownTags::Base64url:
+ return "Base64url";
+ case QCborKnownTags::Base64:
+ return "Base64";
+ case QCborKnownTags::RegularExpression:
+ return "RegularExpression";
+ case QCborKnownTags::MimeMessage:
+ return "MimeMessage";
+ case QCborKnownTags::Uuid:
+ return "Uuid";
+ case QCborKnownTags::COSE_Encrypt:
+ return "COSE_Encrypt";
+ case QCborKnownTags::COSE_Mac:
+ return "COSE_Mac";
+ case QCborKnownTags::COSE_Sign:
+ return "COSE_Sign";
+ case QCborKnownTags::Signature:
+ return "Signature";
+ }
+ }
+ return nullptr;
+}
+
+QDebug operator<<(QDebug dbg, QCborTag tag)
+{
+ QDebugStateSaver saver(dbg);
+ const char *id = qt_cbor_tag_id(tag);
+ dbg.nospace() << "QCborTag(";
+ if (id)
+ dbg.nospace() << "QCborKnownTags::" << id;
+ else
+ dbg.nospace() << quint64(tag);
+
+ return dbg << ')';
+}
+
+QDebug operator<<(QDebug dbg, QCborKnownTags tag)
+{
+ QDebugStateSaver saver(dbg);
+ const char *id = qt_cbor_tag_id(QCborTag(int(tag)));
+ if (id)
+ return dbg.nospace() << "QCborKnownTags::" << id;
+
+ return dbg.nospace() << "QCborKnownTags(" << int(tag) << ')';
+}
#endif
#ifndef QT_NO_DATASTREAM
@@ -2995,4 +3136,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 dcbe7efc26..f7064ac6e1 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;
@@ -88,7 +90,9 @@ public:
enum EncodingOption {
SortKeysInMaps = 0x01,
UseFloat = 0x02,
+#ifndef QT_BOOTSTRAPPED
UseFloat16 = UseFloat | 0x04,
+#endif
UseIntegers = 0x08,
NoTransformation = 0
@@ -161,7 +165,9 @@ public:
{}
explicit QCborValue(const QDateTime &dt);
+#ifndef QT_BOOTSTRAPPED
explicit QCborValue(const QUrl &url);
+#endif
#if QT_CONFIG(regularexpression)
explicit QCborValue(const QRegularExpression &rx);
#endif
@@ -283,20 +289,26 @@ public:
static QCborValue fromJsonValue(const QJsonValue &v);
QJsonValue toJsonValue() const;
+#if QT_CONFIG(cborstreamreader)
static QCborValue fromCbor(QCborStreamReader &reader);
static QCborValue fromCbor(const QByteArray &ba, QCborParserError *error = nullptr);
static QCborValue fromCbor(const char *data, qsizetype len, QCborParserError *error = nullptr)
{ return fromCbor(QByteArray(data, int(len)), error); }
static QCborValue fromCbor(const quint8 *data, qsizetype len, QCborParserError *error = nullptr)
{ return fromCbor(QByteArray(reinterpret_cast<const char *>(data), int(len)), error); }
+#endif // QT_CONFIG(cborstreamreader)
+#if QT_CONFIG(cborstreamwriter)
QByteArray toCbor(EncodingOptions opt = NoTransformation);
void toCbor(QCborStreamWriter &writer, EncodingOptions opt = NoTransformation);
+#endif
QString toDiagnosticNotation(DiagnosticNotationOptions opts = Compact) const;
private:
friend class QCborValueRef;
friend class QCborContainerPrivate;
+ friend class QJsonPrivate::Value;
+
qint64 n = 0;
QCborContainerPrivate *container = nullptr;
Type t = Undefined;
@@ -387,8 +399,10 @@ public:
{ return concrete().toString(defaultValue); }
QDateTime toDateTime(const QDateTime &defaultValue = {}) const
{ return concrete().toDateTime(defaultValue); }
+#ifndef QT_BOOTSTRAPPED
QUrl toUrl(const QUrl &defaultValue = {}) const
{ return concrete().toUrl(defaultValue); }
+#endif
#if QT_CONFIG(regularexpression)
QRegularExpression toRegularExpression(const QRegularExpression &defaultValue = {}) const
{ return concrete().toRegularExpression(defaultValue); }
@@ -431,9 +445,11 @@ public:
QVariant toVariant() const { return concrete().toVariant(); }
QJsonValue toJsonValue() const;
+#if QT_CONFIG(cborstreamwriter)
QByteArray toCbor(QCborValue::EncodingOptions opt = QCborValue::NoTransformation)
{ return concrete().toCbor(opt); }
void toCbor(QCborStreamWriter &writer, QCborValue::EncodingOptions opt = QCborValue::NoTransformation);
+#endif
QString toDiagnosticNotation(QCborValue::DiagnosticNotationOptions opt = QCborValue::Compact)
{ return concrete().toDiagnosticNotation(opt); }
diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp
index b3330d6cea..5082a8cb0d 100644
--- a/src/corelib/serialization/qdatastream.cpp
+++ b/src/corelib/serialization/qdatastream.cpp
@@ -566,6 +566,7 @@ void QDataStream::setByteOrder(ByteOrder bo)
\value Qt_5_12 Version 18 (Qt 5.12)
\value Qt_5_13 Version 19 (Qt 5.13)
\value Qt_5_14 Same as Qt_5_13
+ \value Qt_5_15 Same as Qt_5_13
\omitvalue Qt_DefaultCompiledVersion
\sa setVersion(), version()
diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h
index cfcd89333b..4d827772c8 100644
--- a/src/corelib/serialization/qdatastream.h
+++ b/src/corelib/serialization/qdatastream.h
@@ -301,7 +301,10 @@ QDataStream &readAssociativeContainer(QDataStream &s, Container &c)
c.clear();
break;
}
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
c.insertMulti(k, t);
+QT_WARNING_POP
}
return s;
@@ -321,14 +324,31 @@ template <typename Container>
QDataStream &writeAssociativeContainer(QDataStream &s, const Container &c)
{
s << quint32(c.size());
+#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && QT_DEPRECATED_SINCE(5, 15)
// Deserialization should occur in the reverse order.
// Otherwise, value() will return the least recently inserted
// value instead of the most recently inserted one.
auto it = c.constEnd();
auto begin = c.constBegin();
while (it != begin) {
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_DEPRECATED
--it;
+ QT_WARNING_POP
s << it.key() << it.value();
+#else
+ auto it = c.constBegin();
+ auto end = c.constEnd();
+ while (it != end) {
+ const auto rangeStart = it++;
+ while (it != end && rangeStart.key() == it.key())
+ ++it;
+ const qint64 last = std::distance(rangeStart, it) - 1;
+ for (qint64 i = last; i >= 0; --i) {
+ auto next = std::next(rangeStart, i);
+ s << next.key() << next.value();
+ }
+#endif
}
return s;
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..08702771a8 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,17 +167,19 @@ QJsonArray::QJsonArray(QJsonPrivate::Data *data, QJsonPrivate::Array *array)
*/
void QJsonArray::initialize()
{
- d = nullptr;
a = nullptr;
}
/*!
Deletes the array.
*/
-QJsonArray::~QJsonArray()
+QJsonArray::~QJsonArray() = default;
+
+QJsonArray::QJsonArray(std::initializer_list<QJsonValue> args)
{
- if (d && !d->ref.deref())
- delete d;
+ initialize();
+ for (const auto & arg : args)
+ append(arg);
}
/*!
@@ -189,10 +190,13 @@ QJsonArray::~QJsonArray()
*/
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..ee4dd90416 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();
@@ -623,9 +640,9 @@ static void appendVariant(QCborContainerPrivate *d, const QVariant &variant)
// Handle strings and byte arrays directly, to avoid creating a temporary
// dummy container to hold their data.
int type = variant.userType();
- if (type == QVariant::String) {
+ if (type == QMetaType::QString) {
d->append(variant.toString());
- } else if (type == QVariant::ByteArray) {
+ } else if (type == QMetaType::QByteArray) {
QByteArray ba = variant.toByteArray();
d->appendByteData(ba.constData(), ba.size(), QCborValue::ByteArray);
} else {
@@ -686,43 +703,45 @@ static void appendVariant(QCborContainerPrivate *d, const QVariant &variant)
QCborValue QCborValue::fromVariant(const QVariant &variant)
{
switch (variant.userType()) {
- case QVariant::Invalid:
+ case QMetaType::UnknownType:
return {};
case QMetaType::Nullptr:
return nullptr;
- case QVariant::Bool:
+ case QMetaType::Bool:
return variant.toBool();
case QMetaType::Short:
case QMetaType::UShort:
- case QVariant::Int:
- case QVariant::LongLong:
- case QVariant::ULongLong:
- case QVariant::UInt:
+ case QMetaType::Int:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::UInt:
return variant.toLongLong();
case QMetaType::Float:
- case QVariant::Double:
+ case QMetaType::Double:
return variant.toDouble();
- case QVariant::String:
+ case QMetaType::QString:
return variant.toString();
- case QVariant::StringList:
+ case QMetaType::QStringList:
return QCborArray::fromStringList(variant.toStringList());
- case QVariant::ByteArray:
+ case QMetaType::QByteArray:
return variant.toByteArray();
- case QVariant::DateTime:
+ case QMetaType::QDateTime:
return QCborValue(variant.toDateTime());
- case QVariant::Url:
+#ifndef QT_BOOTSTRAPPED
+ case QMetaType::QUrl:
return QCborValue(variant.toUrl());
- case QVariant::Uuid:
+#endif
+ case QMetaType::QUuid:
return QCborValue(variant.toUuid());
- case QVariant::List:
+ case QMetaType::QVariantList:
return QCborArray::fromVariantList(variant.toList());
- case QVariant::Map:
+ case QMetaType::QVariantMap:
return QCborMap::fromVariantMap(variant.toMap());
- case QVariant::Hash:
+ case QMetaType::QVariantHash:
return QCborMap::fromVariantHash(variant.toHash());
#ifndef QT_BOOTSTRAPPED
#if QT_CONFIG(regularexpression)
- case QVariant::RegularExpression:
+ case QMetaType::QRegularExpression:
return QCborValue(variant.toRegularExpression());
#endif
case QMetaType::QJsonValue:
@@ -738,13 +757,13 @@ QCborValue QCborValue::fromVariant(const QVariant &variant)
return QCborMap::fromJsonObject(doc.object());
}
case QMetaType::QCborValue:
- return variant.value<QCborValue>();
+ return qvariant_cast<QCborValue>(variant);
case QMetaType::QCborArray:
- return variant.value<QCborArray>();
+ return qvariant_cast<QCborArray>(variant);
case QMetaType::QCborMap:
- return variant.value<QCborMap>();
+ return qvariant_cast<QCborMap>(variant);
case QMetaType::QCborSimpleType:
- return variant.value<QCborSimpleType>();
+ return qvariant_cast<QCborSimpleType>(variant);
#endif
default:
break;
@@ -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..0be0fc020d 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) && QT_DEPRECATED_SINCE(5, 15)
/*!
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,67 @@ 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;
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_DEPRECATED
+ const char *raw = rawData(&size);
+QT_WARNING_POP
+ return QByteArray(raw, size);
}
+#endif // QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
/*!
Creates a QJsonDocument from the QVariant \a variant.
@@ -293,18 +404,20 @@ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidati
QJsonDocument QJsonDocument::fromVariant(const QVariant &variant)
{
QJsonDocument doc;
- switch (variant.type()) {
- case QVariant::Map:
+
+ switch (variant.userType()) {
+ case QMetaType::QVariantMap:
doc.setObject(QJsonObject::fromVariantMap(variant.toMap()));
break;
- case QVariant::Hash:
+ case QMetaType::QVariantHash:
doc.setObject(QJsonObject::fromVariantHash(variant.toHash()));
break;
- case QVariant::List:
+ case QMetaType::QVariantList:
doc.setArray(QJsonArray::fromVariantList(variant.toList()));
break;
- case QVariant::StringList:
- doc.setArray(QJsonArray::fromStringList(variant.toStringList()));
+ case QMetaType::QStringList:
+ doc.d = qt_make_unique<QJsonDocumentPrivate>();
+ doc.d->value = QCborArray::fromStringList(variant.toStringList());
break;
default:
break;
@@ -325,10 +438,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 +483,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 +506,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 +527,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 +536,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 +549,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 +562,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 +579,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 +593,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 +608,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 +644,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 +656,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 +676,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 +684,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 +716,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..758bbfd9dd 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,26 @@ 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) && QT_DEPRECATED_SINCE(5, 15)
+ QT_DEPRECATED_X("Use CBOR format instead")
static QJsonDocument fromRawData(const char *data, int size, DataValidation validation = Validate);
+
+ QT_DEPRECATED_X("Use CBOR format instead")
const char *rawData(int *size) const;
+ QT_DEPRECATED_X("Use CBOR format instead")
static QJsonDocument fromBinaryData(const QByteArray &data, DataValidation validation = Validate);
+
+ QT_DEPRECATED_X("Use CBOR format instead")
QByteArray toBinaryData() const;
+#endif // QT_CONFIG(binaryjson) && QT_DEPRECATED_SINCE(5, 15)
static QJsonDocument fromVariant(const QVariant &variant);
QVariant toVariant() const;
@@ -160,13 +164,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 329bc4d2c9..a6987279d3 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();
+}
+
+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;
+ });
- return !o->length;
+ *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
@@ -1092,6 +1013,23 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
Returns a pointer to a modifiable reference to the current item.
*/
+/*! \fn const QJsonValueRef QJsonObject::iterator::operator[](int j)
+
+ Returns a modifiable reference to the item at offset \a j from the
+ item pointed to by this iterator (the item at position \c{*this + j}).
+
+ This function is provided to make QJsonObject iterators behave like C++
+ pointers.
+
+ The return value is of type QJsonValueRef, a helper class for QJsonArray
+ and QJsonObject. When you get an object of type QJsonValueRef, you can
+ use it as if it were a reference to a QJsonValue. If you assign to it,
+ the assignment will apply to the element in the QJsonArray or QJsonObject
+ from which you got the reference.
+
+ \sa operator+()
+*/
+
/*!
\fn bool QJsonObject::iterator::operator==(const iterator &other) const
\fn bool QJsonObject::iterator::operator==(const const_iterator &other) const
@@ -1112,6 +1050,38 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
\sa operator==()
*/
+/*!
+ \fn bool QJsonObject::iterator::operator<(const iterator& other) const
+ \fn bool QJsonObject::iterator::operator<(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool QJsonObject::iterator::operator<=(const iterator& other) const
+ \fn bool QJsonObject::iterator::operator<=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ or equal to the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool QJsonObject::iterator::operator>(const iterator& other) const
+ \fn bool QJsonObject::iterator::operator>(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool QJsonObject::iterator::operator>=(const iterator& other) const
+ \fn bool QJsonObject::iterator::operator>=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than or equal to the item pointed to by the \a other iterator.
+*/
+
/*! \fn QJsonObject::iterator QJsonObject::iterator::operator++()
The prefix ++ operator, \c{++i}, advances the iterator to the
@@ -1185,6 +1155,12 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
\sa operator+=(), operator-()
*/
+/*! \fn int QJsonObject::iterator::operator-(iterator other) const
+
+ Returns the number of items between the item pointed to by \a
+ other and the item pointed to by this iterator.
+*/
+
/*!
\class QJsonObject::const_iterator
\inmodule QtCore
@@ -1288,6 +1264,18 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
Returns a pointer to the current item.
*/
+/*! \fn const QJsonValue QJsonObject::const_iterator::operator[](int j)
+
+ Returns the item at offset \a j from the item pointed to by this iterator (the item at
+ position \c{*this + j}).
+
+ This function is provided to make QJsonObject iterators behave like C++
+ pointers.
+
+ \sa operator+()
+*/
+
+
/*! \fn bool QJsonObject::const_iterator::operator==(const const_iterator &other) const
\fn bool QJsonObject::const_iterator::operator==(const iterator &other) const
@@ -1306,6 +1294,34 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
\sa operator==()
*/
+/*!
+ \fn bool QJsonObject::const_iterator::operator<(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool QJsonObject::const_iterator::operator<=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is less than
+ or equal to the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool QJsonObject::const_iterator::operator>(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than the item pointed to by the \a other iterator.
+*/
+
+/*!
+ \fn bool QJsonObject::const_iterator::operator>=(const const_iterator& other) const
+
+ Returns \c true if the item pointed to by this iterator is greater
+ than or equal to the item pointed to by the \a other iterator.
+*/
+
/*! \fn QJsonObject::const_iterator QJsonObject::const_iterator::operator++()
The prefix ++ operator, \c{++i}, advances the iterator to the
@@ -1386,6 +1402,12 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
\sa operator+=(), operator-()
*/
+/*! \fn int QJsonObject::const_iterator::operator-(const_iterator other) const
+
+ Returns the number of items between the item pointed to by \a
+ other and the item pointed to by this iterator.
+*/
+
/*!
\internal
@@ -1399,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;
}
/*!
@@ -1428,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());
}
/*!
@@ -1450,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);
}
/*!
@@ -1461,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));
}
/*!
@@ -1473,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));
+ }
}
/*!
@@ -1488,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)
@@ -1513,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 05463f6f36..c31be0353d 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);
}
@@ -154,8 +146,14 @@ public:
#else
inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(o, i); }
#endif
+ const QJsonValueRef operator[](int j) { return QJsonValueRef(o, i + j); }
+
inline bool operator==(const iterator &other) const { return i == other.i; }
inline bool operator!=(const iterator &other) const { return i != other.i; }
+ bool operator<(const iterator& other) const { return i < other.i; }
+ bool operator<=(const iterator& other) const { return i <= other.i; }
+ bool operator>(const iterator& other) const { return i > other.i; }
+ bool operator>=(const iterator& other) const { return i >= other.i; }
inline iterator &operator++() { ++i; return *this; }
inline iterator operator++(int) { iterator r = *this; ++i; return r; }
@@ -166,10 +164,15 @@ public:
inline iterator operator-(int j) const { return operator+(-j); }
inline iterator &operator+=(int j) { i += j; return *this; }
inline iterator &operator-=(int j) { i -= j; return *this; }
+ int operator-(iterator j) const { return i - j.i; }
public:
inline bool operator==(const const_iterator &other) const { return i == other.i; }
inline bool operator!=(const const_iterator &other) const { return i != other.i; }
+ bool operator<(const const_iterator& other) const { return i < other.i; }
+ bool operator<=(const const_iterator& other) const { return i <= other.i; }
+ bool operator>(const const_iterator& other) const { return i > other.i; }
+ bool operator>=(const const_iterator& other) const { return i >= other.i; }
};
friend class iterator;
@@ -200,8 +203,14 @@ public:
#else
inline QJsonValuePtr operator->() const { return QJsonValuePtr(o->valueAt(i)); }
#endif
+ const QJsonValue operator[](int j) { return o->valueAt(i + j); }
+
inline bool operator==(const const_iterator &other) const { return i == other.i; }
inline bool operator!=(const const_iterator &other) const { return i != other.i; }
+ bool operator<(const const_iterator& other) const { return i < other.i; }
+ bool operator<=(const const_iterator& other) const { return i <= other.i; }
+ bool operator>(const const_iterator& other) const { return i > other.i; }
+ bool operator>=(const const_iterator& other) const { return i >= other.i; }
inline const_iterator &operator++() { ++i; return *this; }
inline const_iterator operator++(int) { const_iterator r = *this; ++i; return r; }
@@ -212,9 +221,14 @@ public:
inline const_iterator operator-(int j) const { return operator+(-j); }
inline const_iterator &operator+=(int j) { i += j; return *this; }
inline const_iterator &operator-=(int j) { i -= j; return *this; }
+ int operator-(const_iterator j) const { return i - j.i; }
inline bool operator==(const iterator &other) const { return i == other.i; }
inline bool operator!=(const iterator &other) const { return i != other.i; }
+ bool operator<(const iterator& other) const { return i < other.i; }
+ bool operator<=(const iterator& other) const { return i <= other.i; }
+ bool operator>(const iterator& other) const { return i > other.i; }
+ bool operator>=(const iterator& other) const { return i >= other.i; }
};
friend class const_iterator;
@@ -253,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);
@@ -283,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..caf81c79ef 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,91 +114,80 @@ 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.
+ Creates a value of type Double, with value \a v.
*/
-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.
+ Creates a value of type Double, with value \a v.
*/
-QJsonValue::QJsonValue(int n)
- : d(nullptr), t(Double)
+QJsonValue::QJsonValue(int v)
+ : n(v), t(QCborValue::Integer)
{
- this->dbl = n;
}
/*!
\overload
- Creates a value of type Double, with value \a n.
+ Creates a value of type Double, with value \a v.
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
@@ -460,29 +447,29 @@ QJsonValue QJsonValue::fromVariant(const QVariant &variant)
switch (variant.userType()) {
case QMetaType::Nullptr:
return QJsonValue(Null);
- case QVariant::Bool:
+ case QMetaType::Bool:
return QJsonValue(variant.toBool());
- case QVariant::Int:
+ case QMetaType::Int:
case QMetaType::Float:
- case QVariant::Double:
- case QVariant::LongLong:
- case QVariant::ULongLong:
- case QVariant::UInt:
+ case QMetaType::Double:
+ case QMetaType::LongLong:
+ case QMetaType::ULongLong:
+ case QMetaType::UInt:
return QJsonValue(variant.toDouble());
- case QVariant::String:
+ case QMetaType::QString:
return QJsonValue(variant.toString());
- case QVariant::StringList:
+ case QMetaType::QStringList:
return QJsonValue(QJsonArray::fromStringList(variant.toStringList()));
- case QVariant::List:
+ case QMetaType::QVariantList:
return QJsonValue(QJsonArray::fromVariantList(variant.toList()));
- case QVariant::Map:
+ case QMetaType::QVariantMap:
return QJsonValue(QJsonObject::fromVariantMap(variant.toMap()));
- case QVariant::Hash:
+ case QMetaType::QVariantHash:
return QJsonValue(QJsonObject::fromVariantHash(variant.toHash()));
#ifndef QT_BOOTSTRAPPED
- case QVariant::Url:
+ case QMetaType::QUrl:
return QJsonValue(variant.toUrl().toString(QUrl::FullyEncoded));
- case QVariant::Uuid:
+ case QMetaType::QUuid:
return variant.toUuid().toString(QUuid::WithoutBraces);
case QMetaType::QJsonValue:
return variant.toJsonValue();
@@ -495,11 +482,11 @@ QJsonValue QJsonValue::fromVariant(const QVariant &variant)
return doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object());
}
case QMetaType::QCborValue:
- return variant.value<QCborValue>().toJsonValue();
+ return qvariant_cast<QCborValue>(variant).toJsonValue();
case QMetaType::QCborArray:
- return variant.value<QCborArray>().toJsonArray();
+ return qvariant_cast<QCborArray>(variant).toJsonArray();
case QMetaType::QCborMap:
- return variant.value<QCborMap>().toJsonObject();
+ return qvariant_cast<QCborMap>(variant).toJsonObject();
#endif
default:
break;
@@ -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/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp
index cf59cc54c7..b8137e0abd 100644
--- a/src/corelib/serialization/qtextstream.cpp
+++ b/src/corelib/serialization/qtextstream.cpp
@@ -1681,7 +1681,7 @@ QString QTextStream::readLine(qint64 maxlen)
\since 5.5
Reads one line of text from the stream into \a line.
- If \a line is 0, the read line is not stored.
+ If \a line is \nullptr, the read line is not stored.
The maximum allowed line length is set to \a maxlen. If
the stream contains lines longer than this, then the lines will be
@@ -2689,11 +2689,8 @@ QTextStream &QTextStream::operator<<(const void *ptr)
d->params.numberFlags = oldFlags;
return *this;
}
-#if defined(Q_QDOC) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
+
namespace Qt {
-#else
-namespace QTextStreamFunctions {
-#endif
/*!
\relates QTextStream
@@ -3020,7 +3017,7 @@ QTextStream &ws(QTextStream &stream)
return stream;
}
-} // namespace QTextStreamFunctions
+} // namespace Qt
/*!
\fn QTextStreamManipulator qSetFieldWidth(int width)
@@ -3045,11 +3042,7 @@ QTextStream &ws(QTextStream &stream)
#if QT_CONFIG(textcodec)
-#if defined(Q_QDOC) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
namespace Qt {
-#else
-namespace QTextStreamFunctions {
-#endif
/*!
\relates QTextStream
@@ -3064,7 +3057,7 @@ QTextStream &bom(QTextStream &stream)
return stream;
}
-} // namespace QTextStreamFunctions
+} // namespace Qt
/*!
Sets the codec for this stream to \a codec. The codec is used for
@@ -3215,6 +3208,45 @@ QLocale QTextStream::locale() const
return d->locale;
}
+#if QT_DEPRECATED_SINCE(5, 15) && !defined(Q_QDOC)
+// Deprecated source compatible migration versions:
+namespace QTextStreamFunctions {
+QTextStream &bin(QTextStream &s) { return Qt::bin(s); }
+QTextStream &oct(QTextStream &s) { return Qt::oct(s); }
+QTextStream &dec(QTextStream &s) { return Qt::dec(s); }
+QTextStream &hex(QTextStream &s) { return Qt::hex(s); }
+
+QTextStream &showbase(QTextStream &s) { return Qt::showbase(s); }
+QTextStream &forcesign(QTextStream &s) { return Qt::forcesign(s); }
+QTextStream &forcepoint(QTextStream &s) { return Qt::forcepoint(s); }
+QTextStream &noshowbase(QTextStream &s) { return Qt::noshowbase(s); }
+QTextStream &noforcesign(QTextStream &s) { return Qt::noforcesign(s); }
+QTextStream &noforcepoint(QTextStream &s) { return Qt::noforcepoint(s); }
+
+QTextStream &uppercasebase(QTextStream &s) { return Qt::uppercasebase(s); }
+QTextStream &uppercasedigits(QTextStream &s) { return Qt::uppercasedigits(s); }
+QTextStream &lowercasebase(QTextStream &s) { return Qt::lowercasebase(s); }
+QTextStream &lowercasedigits(QTextStream &s) { return Qt::lowercasedigits(s); }
+
+QTextStream &fixed(QTextStream &s) { return Qt::fixed(s); }
+QTextStream &scientific(QTextStream &s) { return Qt::scientific(s); }
+
+QTextStream &left(QTextStream &s) { return Qt::left(s); }
+QTextStream &right(QTextStream &s) { return Qt::right(s); }
+QTextStream &center(QTextStream &s) { return Qt::center(s); }
+
+QTextStream &endl(QTextStream &s) { return Qt::endl(s); }
+QTextStream &flush(QTextStream &s) { return Qt::flush(s); }
+QTextStream &reset(QTextStream &s) { return Qt::reset(s); }
+
+QTextStream &ws(QTextStream &s) { return Qt::ws(s); }
+
+#if QT_CONFIG(textcodec)
+QTextStream &bom(QTextStream &s) { return Qt::bom(s); }
+#endif
+} // namespace QTextStreamFunctions
+#endif
+
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && !defined(Q_QDOC)
// Binary compatible definitions for Qt<5.14
Q_CORE_EXPORT QTextStream &bin(QTextStream &s) { return Qt::bin(s); }
diff --git a/src/corelib/serialization/qtextstream.h b/src/corelib/serialization/qtextstream.h
index 935ec16536..6f93826d8a 100644
--- a/src/corelib/serialization/qtextstream.h
+++ b/src/corelib/serialization/qtextstream.h
@@ -233,13 +233,7 @@ inline QTextStream &operator<<(QTextStream &s, QTextStreamFunction f)
inline QTextStream &operator<<(QTextStream &s, QTextStreamManipulator m)
{ m.exec(s); return s; }
-#if defined(Q_QDOC) || QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
namespace Qt {
-#else
-// This namespace only exists for 'using namespace' declarations.
-namespace QTextStreamFunctions {
-#endif
-
Q_CORE_EXPORT QTextStream &bin(QTextStream &s);
Q_CORE_EXPORT QTextStream &oct(QTextStream &s);
Q_CORE_EXPORT QTextStream &dec(QTextStream &s);
@@ -272,12 +266,36 @@ Q_CORE_EXPORT QTextStream &bom(QTextStream &s);
Q_CORE_EXPORT QTextStream &ws(QTextStream &s);
-} // namespace QTextStreamFunctions
+} // namespace Qt
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && !defined(Q_QDOC)
-namespace Qt {
-using namespace QTextStreamFunctions;
-}
+#if QT_DEPRECATED_SINCE(5, 15) && !defined(Q_QDOC)
+// This namespace only exists for 'using namespace' declarations.
+namespace QTextStreamFunctions {
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::bin") QTextStream &bin(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::oct") QTextStream &oct(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::dec") QTextStream &dec(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::hex") QTextStream &hex(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::showbase") QTextStream &showbase(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::forcesign") QTextStream &forcesign(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::forcepoint") QTextStream &forcepoint(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::noshowbase") QTextStream &noshowbase(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::noforcesign") QTextStream &noforcesign(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::noforcepoint") QTextStream &noforcepoint(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::uppercasebase") QTextStream &uppercasebase(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::uppercasedigits") QTextStream &uppercasedigits(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::lowercasebase") QTextStream &lowercasebase(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::lowercasedigits") QTextStream &lowercasedigits(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::fixed") QTextStream &fixed(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::scientific") QTextStream &scientific(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::left") QTextStream &left(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::right") QTextStream &right(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::center") QTextStream &center(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::endl") QTextStream &endl(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::flush") QTextStream &flush(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::reset") QTextStream &reset(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::bom") QTextStream &bom(QTextStream &s);
+Q_CORE_EXPORT QT_DEPRECATED_X("Use Qt::ws") QTextStream &ws(QTextStream &s);
+} // namespace QTextStreamFunctions
QT_WARNING_PUSH
QT_WARNING_DISABLE_CLANG("-Wheader-hygiene")
@@ -285,7 +303,7 @@ QT_WARNING_DISABLE_CLANG("-Wheader-hygiene")
// conflicting definitions compiler errors.
using namespace QTextStreamFunctions;
QT_WARNING_POP
-#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && !defined(Q_QDOC)
+#endif // QT_DEPRECATED_SINCE(5, 15) && !defined(Q_QDOC)
inline QTextStreamManipulator qSetFieldWidth(int width)
{
diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri
index 4f2dc64e4f..ff653ca8f3 100644
--- a/src/corelib/serialization/serialization.pri
+++ b/src/corelib/serialization/serialization.pri
@@ -3,10 +3,11 @@
HEADERS += \
serialization/qcborarray.h \
serialization/qcborcommon.h \
+ serialization/qcborcommon_p.h \
serialization/qcbormap.h \
+ serialization/qcborstream.h \
serialization/qcborvalue.h \
serialization/qcborvalue_p.h \
- serialization/qcborstream.h \
serialization/qdatastream.h \
serialization/qdatastream_p.h \
serialization/qjson_p.h \
@@ -23,11 +24,10 @@ HEADERS += \
serialization/qxmlutils_p.h
SOURCES += \
- serialization/qcborstream.cpp \
+ serialization/qcborcommon.cpp \
serialization/qcbordiagnostic.cpp \
serialization/qcborvalue.cpp \
serialization/qdatastream.cpp \
- serialization/qjson.cpp \
serialization/qjsoncbor.cpp \
serialization/qjsondocument.cpp \
serialization/qjsonobject.cpp \
@@ -39,6 +39,36 @@ SOURCES += \
serialization/qxmlstream.cpp \
serialization/qxmlutils.cpp
+qtConfig(cborstreamreader): {
+ SOURCES += \
+ serialization/qcborstreamreader.cpp
+
+ HEADERS += \
+ serialization/qcborstreamreader.h
+}
+
+qtConfig(cborstreamwriter): {
+ SOURCES += \
+ serialization/qcborstreamwriter.cpp
+
+ HEADERS += \
+ serialization/qcborstreamwriter.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