diff options
Diffstat (limited to 'src/corelib/serialization')
38 files changed, 4850 insertions, 3813 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..5097f4eb81 100644 --- a/src/corelib/serialization/qjsoncbor.cpp +++ b/src/corelib/serialization/qjsoncbor.cpp @@ -42,6 +42,10 @@ #include "qcborarray.h" #include "qcbormap.h" + +#include "qjsonarray.h" +#include "qjsonobject.h" +#include "qjsondocument.h" #include "qjson_p.h" #include <private/qnumeric_p.h> @@ -149,7 +153,12 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety case QCborValue::Array: case QCborValue::Map: +#if defined(QT_JSON_READONLY) || defined(QT_BOOTSTRAPPED) + qFatal("Writing JSON is disabled."); + return QString(); +#else return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact); +#endif case QCborValue::SimpleType: break; @@ -181,30 +190,11 @@ static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizety return simpleTypeString(e.type); } -static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx); - -static QJsonArray convertToJsonArray(const QCborContainerPrivate *d) -{ - QJsonArray a; - if (d) { - for (qsizetype idx = 0; idx < d->elements.size(); ++idx) - a.append(convertToJson(d, idx)); - } - return a; -} - -static QJsonObject convertToJsonObject(const QCborContainerPrivate *d) -{ - QJsonObject o; - if (d) { - for (qsizetype idx = 0; idx < d->elements.size(); idx += 2) - o.insert(makeString(d, idx), convertToJson(d, idx + 1)); - } - return o; -} +QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx); -static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d) +static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d) { +#ifndef QT_BUILD_QMAKE qint64 tag = d->elements.at(0).value; switch (tag) { @@ -225,12 +215,36 @@ static QJsonValue convertExtendedTypeToJson(const QCborContainerPrivate *d) return s; } } +#endif // for all other tags, ignore it and return the converted tagged item - return convertToJson(d, 1); + return qt_convertToJson(d, 1); +} + +// We need to do this because sub-objects may need conversion. +static QJsonArray convertToJsonArray(QCborContainerPrivate *d) +{ + QJsonArray a; + if (d) { + for (qsizetype idx = 0; idx < d->elements.size(); ++idx) + a.append(qt_convertToJson(d, idx)); + } + return a; } -static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) +// We need to do this because the keys need to be sorted and converted to strings +// and sub-objects may need recursive conversion. +static QJsonObject convertToJsonObject(QCborContainerPrivate *d) +{ + QJsonObject o; + if (d) { + for (qsizetype idx = 0; idx < d->elements.size(); idx += 2) + o.insert(makeString(d, idx), qt_convertToJson(d, idx + 1)); + } + return o; +} + +QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx) { // encoding the container itself if (idx == -QCborValue::Array) @@ -248,7 +262,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) const auto &e = d->elements.at(idx); switch (e.type) { case QCborValue::Integer: - return qint64(e.value); + return QJsonPrivate::Value::fromTrustedCbor(e.value); case QCborValue::ByteArray: case QCborValue::String: @@ -264,14 +278,14 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) case QCborValue::RegularExpression: case QCborValue::Uuid: // recurse - return convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type); + return qt_convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type); case QCborValue::Null: return QJsonValue(); case QCborValue::Undefined: case QCborValue::Invalid: - return QJsonValue(QJsonValue::Undefined); + return QJsonValue::Undefined; case QCborValue::False: return false; @@ -283,7 +297,7 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) return fpToJson(e.fpvalue()); } - return makeString(d, idx); + return QJsonPrivate::Value::fromTrustedCbor(makeString(d, idx)); } /*! @@ -348,22 +362,22 @@ static QJsonValue convertToJson(const QCborContainerPrivate *d, qsizetype idx) QJsonValue QCborValue::toJsonValue() const { if (container) - return convertToJson(container, n < 0 ? -type() : n); + return qt_convertToJson(container, n < 0 ? -type() : n); // simple values switch (type()) { - case Integer: - return n; - - case Null: - return QJsonValue(); - case False: return false; + case Integer: + return QJsonPrivate::Value::fromTrustedCbor(n); + case True: return true; + case Null: + return QJsonValue(); + case Double: return fpToJson(fp_helper()); @@ -372,12 +386,12 @@ QJsonValue QCborValue::toJsonValue() const case Undefined: case Invalid: - return QJsonValue(QJsonValue::Undefined); + return QJsonValue::Undefined; case ByteArray: case String: // empty strings - return QString(); + return QJsonValue::String; case Array: // empty array @@ -392,16 +406,16 @@ QJsonValue QCborValue::toJsonValue() const case Url: case RegularExpression: case Uuid: - Q_UNREACHABLE(); + // Reachable, but invalid in Json return QJsonValue::Undefined; } - return simpleTypeString(type()); + return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type())); } QJsonValue QCborValueRef::toJsonValue() const { - return convertToJson(d, i); + return qt_convertToJson(d, i); } /*! @@ -540,8 +554,10 @@ QVariant QCborValue::toVariant() const case DateTime: return toDateTime(); +#ifndef QT_BOOTSTRAPPED case Url: return toUrl(); +#endif #if QT_CONFIG(regularexpression) case RegularExpression: @@ -597,12 +613,13 @@ QCborValue QCborValue::fromJsonValue(const QJsonValue &v) { switch (v.type()) { case QJsonValue::Bool: - return v.b; + return v.toBool(); case QJsonValue::Double: { qint64 i; - if (convertDoubleTo(v.dbl, &i)) + const double dbl = v.toDouble(); + if (convertDoubleTo(dbl, &i)) return i; - return v.dbl; + return dbl; } case QJsonValue::String: return v.toString(); @@ -710,8 +727,10 @@ QCborValue QCborValue::fromVariant(const QVariant &variant) return variant.toByteArray(); case QVariant::DateTime: return QCborValue(variant.toDateTime()); +#ifndef QT_BOOTSTRAPPED case QVariant::Url: return QCborValue(variant.toUrl()); +#endif case QVariant::Uuid: return QCborValue(variant.toUuid()); case QVariant::List: @@ -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..fe0500bdef 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,6 +404,7 @@ QJsonDocument QJsonDocument::fromBinaryData(const QByteArray &data, DataValidati QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) { QJsonDocument doc; + switch (variant.type()) { case QVariant::Map: doc.setObject(QJsonObject::fromVariantMap(variant.toMap())); @@ -304,7 +416,8 @@ QJsonDocument QJsonDocument::fromVariant(const QVariant &variant) doc.setArray(QJsonArray::fromVariantList(variant.toList())); break; case QVariant::StringList: - doc.setArray(QJsonArray::fromStringList(variant.toStringList())); + doc.d = qt_make_unique<QJsonDocumentPrivate>(); + doc.d->value = QCborArray::fromStringList(variant.toStringList()); break; default: break; @@ -325,10 +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..7b6728d525 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 @@ -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..4d92b1e0da 100644 --- a/src/corelib/serialization/qtextstream.cpp +++ b/src/corelib/serialization/qtextstream.cpp @@ -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 ¢er(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 ¢er(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 |