diff options
Diffstat (limited to 'src/corelib/serialization')
23 files changed, 808 insertions, 53 deletions
diff --git a/src/corelib/serialization/qcborarray.cpp b/src/corelib/serialization/qcborarray.cpp index c56536cdb1..28ae40f3df 100644 --- a/src/corelib/serialization/qcborarray.cpp +++ b/src/corelib/serialization/qcborarray.cpp @@ -39,6 +39,7 @@ #include "qcborarray.h" #include "qcborvalue_p.h" +#include "qdatastream.h" QT_BEGIN_NAMESPACE @@ -1218,4 +1219,23 @@ QDebug operator<<(QDebug dbg, const QCborArray &a) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QCborArray &value) +{ + stream << value.toCborValue().toCbor(); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QCborArray &value) +{ + QByteArray buffer; + stream >> buffer; + QCborParserError parseError{}; + value = QCborValue::fromCbor(buffer, &parseError).toArray(); + if (parseError.error) + stream.setStatus(QDataStream::ReadCorruptData); + return stream; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/serialization/qcborarray.h b/src/corelib/serialization/qcborarray.h index ed0f4912ba..e06544f245 100644 --- a/src/corelib/serialization/qcborarray.h +++ b/src/corelib/serialization/qcborarray.h @@ -47,6 +47,7 @@ QT_BEGIN_NAMESPACE class QJsonArray; +class QDataStream; class QCborContainerPrivate; class Q_CORE_EXPORT QCborArray @@ -271,6 +272,7 @@ private: void detach(qsizetype reserve = 0); friend QCborValue; + friend QCborValueRef; explicit QCborArray(QCborContainerPrivate &dd) noexcept; QExplicitlySharedDataPointer<QCborContainerPrivate> d; }; @@ -298,6 +300,11 @@ Q_CORE_EXPORT uint qHash(const QCborArray &array, uint seed = 0); Q_CORE_EXPORT QDebug operator<<(QDebug, const QCborArray &a); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QCborArray &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QCborArray &); +#endif + QT_END_NAMESPACE #endif // QCBORARRAY_H diff --git a/src/corelib/serialization/qcborcommon.h b/src/corelib/serialization/qcborcommon.h index f8278f1649..3dfe50cd09 100644 --- a/src/corelib/serialization/qcborcommon.h +++ b/src/corelib/serialization/qcborcommon.h @@ -133,6 +133,11 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, QCborKnownTags tg); Q_CORE_EXPORT QDebug operator<<(QDebug, QCborTag tg); #endif +#if !defined(QT_NO_DEBUG_STREAM) +QDataStream &operator<<(QDataStream &ds, QCborSimpleType st); +QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st); +#endif + inline uint qHash(QCborSimpleType tag, uint seed = 0) { return qHash(quint8(tag), seed); diff --git a/src/corelib/serialization/qcbormap.cpp b/src/corelib/serialization/qcbormap.cpp index 33f9249993..4b28ca4a2e 100644 --- a/src/corelib/serialization/qcbormap.cpp +++ b/src/corelib/serialization/qcbormap.cpp @@ -1763,4 +1763,23 @@ QDebug operator<<(QDebug dbg, const QCborMap &m) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QCborMap &value) +{ + stream << value.toCborValue().toCbor(); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QCborMap &value) +{ + QByteArray buffer; + stream >> buffer; + QCborParserError parseError{}; + value = QCborValue::fromCbor(buffer, &parseError).toMap(); + if (parseError.error) + stream.setStatus(QDataStream::ReadCorruptData); + return stream; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/serialization/qcbormap.h b/src/corelib/serialization/qcbormap.h index 3eb2107691..4aea901eef 100644 --- a/src/corelib/serialization/qcbormap.h +++ b/src/corelib/serialization/qcbormap.h @@ -52,6 +52,7 @@ typedef QMap<QString, QVariant> QVariantMap; template <class Key, class T> class QHash; typedef QHash<QString, QVariant> QVariantHash; class QJsonObject; +class QDataStream; class QCborContainerPrivate; class Q_CORE_EXPORT QCborMap @@ -117,6 +118,8 @@ public: QCborValueRef item; // points to the value friend class Iterator; friend class QCborMap; + friend class QCborValue; + friend class QCborValueRef; ConstIterator(QCborContainerPrivate *dd, qsizetype ii) : item(dd, ii) {} public: typedef std::random_access_iterator_tag iterator_category; @@ -170,7 +173,7 @@ public: : QCborMap() { detach(args.size()); - for (auto pair : args) + for (const auto &pair : args) insert(pair.first, pair.second); } ~QCborMap(); @@ -322,9 +325,10 @@ public: QJsonObject toJsonObject() const; private: + friend class QCborValue; + friend class QCborValueRef; void detach(qsizetype reserve = 0); - friend QCborValue; explicit QCborMap(QCborContainerPrivate &dd) noexcept; QExplicitlySharedDataPointer<QCborContainerPrivate> d; }; @@ -352,6 +356,12 @@ Q_CORE_EXPORT uint qHash(const QCborMap &map, uint seed = 0); Q_CORE_EXPORT QDebug operator<<(QDebug, const QCborMap &m); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QCborMap &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QCborMap &); +#endif + + QT_END_NAMESPACE #endif // QCBORMAP_H diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstream.cpp index df38118805..1392b4d8d6 100644 --- a/src/corelib/serialization/qcborstream.cpp +++ b/src/corelib/serialization/qcborstream.cpp @@ -44,6 +44,7 @@ #include <qbuffer.h> #include <qdebug.h> #include <qstack.h> +#include <qdatastream.h> QT_BEGIN_NAMESPACE @@ -180,6 +181,21 @@ Q_CORE_EXPORT const char *qt_cbor_simpletype_id(QCborSimpleType st) 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) { diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 80ef515fd2..288446878c 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -39,7 +39,7 @@ #include "qcborvalue.h" #include "qcborvalue_p.h" - +#include "qdatastream.h" #include "qcborarray.h" #include "qcbormap.h" #include "qcborstream.h" @@ -108,7 +108,7 @@ QT_BEGIN_NAMESPACE QCborValue can contain a value of "null", which is not of any specific type. It resembles the C++ \c {std::nullptr_t} type, whose only possible value is - \c nullptr. QCborValue has a constructor taking such a type and creates a + \nullptr. QCborValue has a constructor taking such a type and creates a null QCborValue. Null values are used to indicate that an optional value is not present. In @@ -128,10 +128,11 @@ QT_BEGIN_NAMESPACE Such values are completely valid and may appear in CBOR streams, unlike JSON content and QJsonValue's undefined bit. But like QJsonValue's - Undefined, it is returned by QCborArray::value() when out of range or - QCborMap::operator[] when the key is not found in the container. It is not - possible to tell such a case apart from the value of Undefined, so if that - is required, check the QCborArray size and use the QCborMap iterator API. + Undefined, it is returned by a CBOR container's value() or read-only + operator[] for invalid look-ups (index out of range for QCborArray, or key + not found for QCborMap). It is not possible to tell such a case apart from + the value of Undefined, so if that is required, check the QCborArray size + and use the QCborMap iterator API. \section1 Simple types @@ -416,7 +417,7 @@ QT_BEGIN_NAMESPACE using toSimpleType() as well as isSimpleType(st). CBOR simple types are types that do not have any associated value, like - C++'s \c{std::nullptr_t} type, whose only possible value is \c nullptr. + C++'s \c{std::nullptr_t} type, whose only possible value is \nullptr. If \a st is \c{QCborSimpleType::Null}, the resulting QCborValue will be of the \l{Type}{Null} type and similarly for \c{QCborSimpleType::Undefined}. @@ -457,7 +458,7 @@ QT_BEGIN_NAMESPACE \fn QCborValue::QCborValue(QCborValue &&other) \overload - Moves the contents of the \a other CBorValue object into this one and frees + Moves the contents of the \a other QCborValue object into this one and frees the resources of this one. */ @@ -465,7 +466,7 @@ QT_BEGIN_NAMESPACE \fn QCborValue &&QCborValue::operator=(QCborValue &&other) \overload - Moves the contents of the \a other CBorValue object into this one and frees + Moves the contents of the \a other QCborValue object into this one and frees the resources of this one. Returns a reference to this object. */ @@ -1986,12 +1987,24 @@ QUuid QCborValue::toUuid(const QUuid &defaultValue) const return QUuid::fromRfc4122(byteData->asByteArrayView()); } -QCborArray QCborValue::toArray() const -{ - return toArray(QCborArray()); -} +/*! + \fn QCborArray QCborValue::toArray() const + \fn QCborArray QCborValue::toArray(const QCborArray &defaultValue) const + + Returns the array value stored in this QCborValue, if it is of the array + type. Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to + QCborArray. + + \sa isArray(), isByteArray(), isMap(), isContainer(), toMap() + */ /*! + \fn QCborArray QCborValueRef::toArray() const + \fn QCborArray QCborValueRef::toArray(const QCborArray &defaultValue) const + \internal + Returns the array value stored in this QCborValue, if it is of the array type. Otherwise, it returns \a defaultValue. @@ -2000,6 +2013,11 @@ QCborArray QCborValue::toArray() const \sa isArray(), isByteArray(), isMap(), isContainer(), toMap() */ +QCborArray QCborValue::toArray() const +{ + return toArray(QCborArray()); +} + QCborArray QCborValue::toArray(const QCborArray &defaultValue) const { if (!isArray()) @@ -2011,12 +2029,24 @@ QCborArray QCborValue::toArray(const QCborArray &defaultValue) const return dd ? QCborArray(*dd) : defaultValue; } -QCborMap QCborValue::toMap() const -{ - return toMap(QCborMap()); -} +/*! + \fn QCborMap QCborValue::toMap() const + \fn QCborMap QCborValue::toMap(const QCborMap &defaultValue) const + + Returns the map value stored in this QCborValue, if it is of the map type. + Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to + QCborMap. + + \sa isMap(), isArray(), isContainer(), toArray() + */ /*! + \fn QCborMap QCborValueRef::toMap() const + \fn QCborMap QCborValueRef::toMap(const QCborMap &defaultValue) const + \internal + Returns the map value stored in this QCborValue, if it is of the map type. Otherwise, it returns \a defaultValue. @@ -2025,6 +2055,11 @@ QCborMap QCborValue::toMap() const \sa isMap(), isArray(), isContainer(), toArray() */ +QCborMap QCborValue::toMap() const +{ + return toMap(QCborMap()); +} + QCborMap QCborValue::toMap(const QCborMap &defaultValue) const { if (!isMap()) @@ -2077,15 +2112,16 @@ const QCborValue QCborValue::operator[](QLatin1String key) const } /*! + \overload + If this QCborValue is a QCborMap, searches elements for the value whose key - matches \a key. If this is an array, returns the element whose index is \a - key. If there's no matching value in the array or map, or if this + matches \a key. If this is a QCborArray, returns the element whose index is + \a key. If there's no matching value in the array or map, or if this QCborValue object is not an array or map, returns the undefined value. \sa operator[], QCborMap::operator[], QCborMap::value(), QCborMap::find(), QCborArray::operator[], QCborArray::at() */ - const QCborValue QCborValue::operator[](qint64 key) const { if (isMap()) @@ -2096,6 +2132,191 @@ const QCborValue QCborValue::operator[](qint64 key) const } /*! + \internal + */ +static Q_DECL_COLD_FUNCTION QCborMap arrayAsMap(const QCborArray &array) +{ + if (array.size()) + qWarning("Using CBOR array as map forced conversion"); + QCborMap map; + for (qsizetype i = array.size(); i-- > 0; ) { + QCborValue entry = array.at(i); + // Ignore padding entries that may have been added to grow the array + // when inserting past its end: + if (!entry.isInvalid()) + map[i] = entry; + } + return map; +} + +/*! + \internal + */ +static QCborContainerPrivate *maybeDetach(QCborContainerPrivate *container, qsizetype size) +{ + auto replace = QCborContainerPrivate::detach(container, size); + Q_ASSERT(replace); + if (replace != container) { + if (container) + container->deref(); + replace->ref.ref(); + } + return replace; +} + +/*! + \internal + */ +static QCborContainerPrivate *maybeGrow(QCborContainerPrivate *container, qsizetype index) +{ + auto replace = QCborContainerPrivate::grow(container, index); + Q_ASSERT(replace); + if (replace != container) { + if (container) + container->deref(); + replace->ref.ref(); + } + if (replace->elements.size() == index) + replace->append(Undefined()); + else + Q_ASSERT(replace->elements.size() > index); + return replace; +} + +/*! + Returns a QCborValueRef that can be used to read or modify the entry in + this, as a map, with the given \a key. When this QCborValue is a QCborMap, + this function is equivalent to the matching operator[] on that map. + + Before returning the reference: if this QCborValue was an array, it is first + converted to a map (so that \c{map[i]} is \c{array[i]} for each index, \c i, + with valid \c{array[i]}); otherwise, if it was not a map it will be + over-written with an empty map. + + \sa operator[](qint64), QCborMap::operator[], QCborMap::value(), + QCborMap::find() + */ +QCborValueRef QCborValue::operator[](const QString &key) +{ + if (!isMap()) + *this = QCborValue(isArray() ? arrayAsMap(toArray()) : QCborMap()); + + const qsizetype size = container ? container->elements.size() : 0; + qsizetype index = size + 1; + bool found = false; + if (container) { + QCborMap proxy(*container); + auto it = proxy.constFind(key); + if (it < proxy.constEnd()) { + found = true; + index = it.item.i; + } + } + + container = maybeDetach(container, size + (found ? 0 : 2)); + Q_ASSERT(container); + if (!found) { + container->append(key); + container->append(QCborValue()); + } + Q_ASSERT(index & 1 && !(container->elements.size() & 1)); + Q_ASSERT(index < container->elements.size()); + return { container, index }; +} + +/*! + \overload + + Returns a QCborValueRef that can be used to read or modify the entry in + this, as a map, with the given \a key. When this QCborValue is a QCborMap, + this function is equivalent to the matching operator[] on that map. + + Before returning the reference: if this QCborValue was an array, it is first + converted to a map (so that \c{map[i]} is \c{array[i]} for each index, \c i, + with valid \c{array[i]}); otherwise, if it was not a map it will be + over-written with an empty map. + + \sa operator[](qint64), QCborMap::operator[], QCborMap::value(), + QCborMap::find() + */ +QCborValueRef QCborValue::operator[](QLatin1String key) +{ + if (!isMap()) + *this = QCborValue(isArray() ? arrayAsMap(toArray()) : QCborMap()); + + const qsizetype size = container ? container->elements.size() : 0; + qsizetype index = size + 1; + bool found = false; + if (container) { + QCborMap proxy(*container); + auto it = proxy.constFind(key); + if (it < proxy.constEnd()) { + found = true; + index = it.item.i; + } + } + + container = maybeDetach(container, size + (found ? 0 : 2)); + Q_ASSERT(container); + if (!found) { + container->append(key); + container->append(QCborValue()); + } + Q_ASSERT(index & 1 && !(container->elements.size() & 1)); + Q_ASSERT(index < container->elements.size()); + return { container, index }; +} + +/*! + \overload + + Returns a QCborValueRef that can be used to read or modify the entry in + this, as a map or array, with the given \a key. When this QCborValue is a + QCborMap or, for 0 <= key < 0x10000, a QCborArray, this function is + equivalent to the matching operator[] on that map or array. + + Before returning the reference: if this QCborValue was an array but the key + is out of range, the array is first converted to a map (so that \c{map[i]} + is \c{array[i]} for each index, \c i, with valid \c{array[i]}); otherwise, + if it was not a map it will be over-written with an empty map. + + \sa operator[], QCborMap::operator[], QCborMap::value(), + QCborMap::find(), QCborArray::operator[], QCborArray::at() + */ +QCborValueRef QCborValue::operator[](qint64 key) +{ + if (isArray() && key >= 0 && key < 0x10000) { + container = maybeGrow(container, key); + return { container, qsizetype(key) }; + } + if (!isMap()) + *this = QCborValue(isArray() ? arrayAsMap(toArray()) : QCborMap()); + + const qsizetype size = container ? container->elements.size() : 0; + Q_ASSERT(!(size & 1)); + qsizetype index = size + 1; + bool found = false; + if (container) { + QCborMap proxy(*container); + auto it = proxy.constFind(key); + if (it < proxy.constEnd()) { + found = true; + index = it.item.i; + } + } + + container = maybeDetach(container, size + (found ? 0 : 2)); + Q_ASSERT(container); + if (!found) { + container->append(key); + container->append(QCborValue()); + } + Q_ASSERT(index & 1 && !(container->elements.size() & 1)); + Q_ASSERT(index < container->elements.size()); + return { container, index }; +} + +/*! 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 or array, it will decode all items found in that map or array, until the @@ -2359,6 +2580,255 @@ QCborValue::Type QCborValueRef::concreteType(QCborValueRef self) noexcept return self.d->elements.at(self.i).type; } +/*! + If this QCborValueRef refers to a QCborMap, searches elements for the value + whose key matches \a key. If there's no key matching \a key in the map or if + this QCborValueRef object is not a map, returns the undefined value. + + This function is equivalent to: + + \code + value.toMap().value(key); + \endcode + + \sa operator[](qint64), QCborMap::operator[], QCborMap::value(), + QCborMap::find() + */ +const QCborValue QCborValueRef::operator[](const QString &key) const +{ + const QCborValue item = d->valueAt(i); + return item[key]; +} + +/*! + \overload + + If this QCborValueRef refers to a QCborMap, searches elements for the value + whose key matches \a key. If there's no key matching \a key in the map or if + this QCborValueRef object is not a map, returns the undefined value. + + This function is equivalent to: + + \code + value.toMap().value(key); + \endcode + + \sa operator[](qint64), QCborMap::operator[], QCborMap::value(), + QCborMap::find() + */ +const QCborValue QCborValueRef::operator[](QLatin1String key) const +{ + const QCborValue item = d->valueAt(i); + return item[key]; +} + +/*! + \overload + + If this QCborValueRef refers to a QCborMap, searches elements for the value + whose key matches \a key. If this is a QCborArray, returns the element whose + index is \a key. If there's no matching value in the array or map, or if + this QCborValueRef object is not an array or map, returns the undefined + value. + + \sa operator[], QCborMap::operator[], QCborMap::value(), + QCborMap::find(), QCborArray::operator[], QCborArray::at() + */ +const QCborValue QCborValueRef::operator[](qint64 key) const +{ + const QCborValue item = d->valueAt(i); + return item[key]; +} + +/*! + Returns a QCborValueRef that can be used to read or modify the entry in + this, as a map, with the given \a key. When this QCborValueRef refers to a + QCborMap, this function is equivalent to the matching operator[] on that + map. + + Before returning the reference: if the QCborValue referenced was an array, + it is first converted to a map (so that \c{map[i]} is \c{array[i]} for each + index, \c i, with valid \c{array[i]}); otherwise, if it was not a map it + will be over-written with an empty map. + + \sa operator[](qint64), QCborMap::operator[], QCborMap::value(), + QCborMap::find() + */ +QCborValueRef QCborValueRef::operator[](const QString &key) +{ + auto &e = d->elements[i]; + qsizetype size = 0; + if (e.flags & QtCbor::Element::IsContainer) { + if (e.container) { + if (e.type == QCborValue::Array) { + QCborValue repack = QCborValue(arrayAsMap(QCborArray(*e.container))); + qSwap(e.container, repack.container); + } else if (e.type != QCborValue::Map) { + e.container->deref(); + e.container = nullptr; + } + } + e.type = QCborValue::Map; + if (e.container) + size = e.container->elements.size(); + } else { + // Stomp any prior e.value, replace with a map (that we'll grow) + e.container = nullptr; + e.type = QCborValue::Map; + e.flags = QtCbor::Element::IsContainer; + } + + qsizetype index = size + 1; + bool found = false; + if (e.container) { + QCborMap proxy(*e.container); + auto it = proxy.constFind(key); + if (it < proxy.constEnd()) { + found = true; + index = it.item.i; + } + } + + e.container = maybeDetach(e.container, size + (found ? 0 : 2)); + Q_ASSERT(e.container); + if (!found) { + e.container->append(key); + e.container->append(QCborValue()); + } + Q_ASSERT(index & 1 && !(e.container->elements.size() & 1)); + Q_ASSERT(index < e.container->elements.size()); + return { e.container, index }; +} + +/*! + \overload + + Returns a QCborValueRef that can be used to read or modify the entry in + this, as a map, with the given \a key. When this QCborValue is a QCborMap, + this function is equivalent to the matching operator[] on that map. + + Before returning the reference: if the QCborValue referenced was an array, + it is first converted to a map (so that \c{map[i]} is \c{array[i]} for each + index, \c i, with valid \c{array[i]}); otherwise, if it was not a map it + will be over-written with an empty map. + + \sa operator[](qint64), QCborMap::operator[], QCborMap::value(), + QCborMap::find() + */ +QCborValueRef QCborValueRef::operator[](QLatin1String key) +{ + auto &e = d->elements[i]; + qsizetype size = 0; + if (e.flags & QtCbor::Element::IsContainer) { + if (e.container) { + if (e.type == QCborValue::Array) { + QCborValue repack = QCborValue(arrayAsMap(QCborArray(*e.container))); + qSwap(e.container, repack.container); + } else if (e.type != QCborValue::Map) { + e.container->deref(); + e.container = nullptr; + } + } + e.type = QCborValue::Map; + if (e.container) + size = e.container->elements.size(); + } else { + // Stomp any prior e.value, replace with a map (that we'll grow) + e.container = nullptr; + e.type = QCborValue::Map; + e.flags = QtCbor::Element::IsContainer; + } + + qsizetype index = size + 1; + bool found = false; + if (e.container) { + QCborMap proxy(*e.container); + auto it = proxy.constFind(key); + if (it < proxy.constEnd()) { + found = true; + index = it.item.i; + } + } + + e.container = maybeDetach(e.container, size + (found ? 0 : 2)); + Q_ASSERT(e.container); + if (!found) { + e.container->append(key); + e.container->append(QCborValue()); + } + Q_ASSERT(index & 1 && !(e.container->elements.size() & 1)); + Q_ASSERT(index < e.container->elements.size()); + return { e.container, index }; +} + +/*! + \overload + + Returns a QCborValueRef that can be used to read or modify the entry in + this, as a map or array, with the given \a key. When this QCborValue is a + QCborMap or, for 0 <= key < 0x10000, a QCborArray, this function is + equivalent to the matching operator[] on that map or array. + + Before returning the reference: if the QCborValue referenced was an array + but the key is out of range, the array is first converted to a map (so that + \c{map[i]} is \c{array[i]} for each index, \c i, with valid \c{array[i]}); + otherwise, if it was not a map it will be over-written with an empty map. + + \sa operator[], QCborMap::operator[], QCborMap::value(), + QCborMap::find(), QCborArray::operator[], QCborArray::at() + */ +QCborValueRef QCborValueRef::operator[](qint64 key) +{ + auto &e = d->elements[i]; + if (e.type == QCborValue::Array && key >= 0 && key < 0x10000) { + e.container = maybeGrow(e.container, key); + return { e.container, qsizetype(key) }; + } + qsizetype size = 0; + if (e.flags & QtCbor::Element::IsContainer) { + if (e.container) { + if (e.type == QCborValue::Array) { + QCborValue repack = QCborValue(arrayAsMap(QCborArray(*e.container))); + qSwap(e.container, repack.container); + } else if (e.type != QCborValue::Map) { + e.container->deref(); + e.container = nullptr; + } + } + e.type = QCborValue::Map; + if (e.container) + size = e.container->elements.size(); + } else { + // Stomp any prior e.value, replace with a map (that we'll grow) + e.container = nullptr; + e.type = QCborValue::Map; + e.flags = QtCbor::Element::IsContainer; + } + Q_ASSERT(!(size & 1)); + + qsizetype index = size + 1; + bool found = false; + if (e.container) { + QCborMap proxy(*e.container); + auto it = proxy.constFind(key); + if (it < proxy.constEnd()) { + found = true; + index = it.item.i; + } + } + + e.container = maybeDetach(e.container, size + (found ? 0 : 2)); + Q_ASSERT(e.container); + if (!found) { + e.container->append(key); + e.container->append(QCborValue()); + } + Q_ASSERT(index & 1 && !(e.container->elements.size() & 1)); + Q_ASSERT(index < e.container->elements.size()); + return { e.container, index }; +} + + inline QCborArray::QCborArray(QCborContainerPrivate &dd) noexcept : d(&dd) { @@ -2481,6 +2951,26 @@ QDebug operator<<(QDebug dbg, const QCborValue &v) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QCborValue &value) +{ + stream << QCborValue(value).toCbor(); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QCborValue &value) +{ + QByteArray buffer; + stream >> buffer; + QCborParserError parseError{}; + value = QCborValue::fromCbor(buffer, &parseError); + if (parseError.error) + stream.setStatus(QDataStream::ReadCorruptData); + return stream; +} +#endif + + QT_END_NAMESPACE #include "qcborarray.cpp" diff --git a/src/corelib/serialization/qcborvalue.h b/src/corelib/serialization/qcborvalue.h index d6ba4e88d8..f542e44c47 100644 --- a/src/corelib/serialization/qcborvalue.h +++ b/src/corelib/serialization/qcborvalue.h @@ -69,6 +69,7 @@ class QCborArray; class QCborMap; class QCborStreamReader; class QCborStreamWriter; +class QDataStream; struct QCborParserError { @@ -78,6 +79,7 @@ struct QCborParserError QString errorString() const { return error.toString(); } }; +class QCborValueRef; class QCborContainerPrivate; class Q_CORE_EXPORT QCborValue { @@ -242,20 +244,18 @@ public: #endif QUuid toUuid(const QUuid &defaultValue = {}) const; -#ifdef Q_QDOC - QCborArray toArray(const QCborArray &a = {}) const; - QCborMap toMap(const QCborMap &m = {}) const; -#else // only forward-declared, need split functions QCborArray toArray() const; QCborArray toArray(const QCborArray &defaultValue) const; QCborMap toMap() const; QCborMap toMap(const QCborMap &defaultValue) const; -#endif const QCborValue operator[](const QString &key) const; const QCborValue operator[](QLatin1String key) const; const QCborValue operator[](qint64 key) const; + QCborValueRef operator[](qint64 key); + QCborValueRef operator[](QLatin1String key); + QCborValueRef operator[](const QString & key); int compare(const QCborValue &other) const; #if 0 && QT_HAS_INCLUDE(<compare>) @@ -393,16 +393,18 @@ public: QUuid toUuid(const QUuid &defaultValue = {}) const { return concrete().toUuid(defaultValue); } -#ifdef Q_QDOC - QCborArray toArray(const QCborArray &a = {}) const; - QCborMap toMap(const QCborMap &m = {}) const; -#else // only forward-declared, need split functions. Implemented in qcbor{array,map}.h QCborArray toArray() const; QCborArray toArray(const QCborArray &a) const; QCborMap toMap() const; QCborMap toMap(const QCborMap &m) const; -#endif + + const QCborValue operator[](const QString &key) const; + const QCborValue operator[](QLatin1String key) const; + const QCborValue operator[](qint64 key) const; + QCborValueRef operator[](qint64 key); + QCborValueRef operator[](QLatin1String key); + QCborValueRef operator[](const QString & key); int compare(const QCborValue &other) const { return concrete().compare(other); } @@ -434,6 +436,7 @@ public: { return concrete().toDiagnosticNotation(opt); } private: + friend class QCborValue; friend class QCborArray; friend class QCborMap; friend class QCborContainerPrivate; @@ -465,6 +468,11 @@ Q_CORE_EXPORT uint qHash(const QCborValue &value, uint seed = 0); Q_CORE_EXPORT QDebug operator<<(QDebug, const QCborValue &v); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QCborValue &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QCborValue &); +#endif + QT_END_NAMESPACE #if defined(QT_X11_DEFINES_FOUND) diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp index 951f6c9736..ead6ed5083 100644 --- a/src/corelib/serialization/qdatastream.cpp +++ b/src/corelib/serialization/qdatastream.cpp @@ -368,7 +368,7 @@ QDataStream::~QDataStream() /*! \fn QIODevice *QDataStream::device() const - Returns the I/O device currently set, or 0 if no + Returns the I/O device currently set, or \nullptr if no device is currently set. \sa setDevice() @@ -377,7 +377,7 @@ QDataStream::~QDataStream() /*! void QDataStream::setDevice(QIODevice *d) - Sets the I/O device to \a d, which can be 0 + Sets the I/O device to \a d, which can be \nullptr to unset to current I/O device. \sa device() @@ -392,17 +392,18 @@ void QDataStream::setDevice(QIODevice *d) dev = d; } +#if QT_DEPRECATED_SINCE(5, 13) /*! \obsolete Unsets the I/O device. - Use setDevice(0) instead. + Use setDevice(nullptr) instead. */ void QDataStream::unsetDevice() { - setDevice(0); + setDevice(nullptr); } - +#endif /*! \fn bool QDataStream::atEnd() const @@ -559,6 +560,7 @@ void QDataStream::setByteOrder(ByteOrder bo) \value Qt_5_10 Same as Qt_5_6 \value Qt_5_11 Same as Qt_5_6 \value Qt_5_12 Version 18 (Qt 5.12) + \value Qt_5_13 Version 19 (Qt 5.13) \omitvalue Qt_DefaultCompiledVersion \sa setVersion(), version() diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index eae0146553..81134f74b0 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -99,10 +99,11 @@ public: Qt_5_10 = Qt_5_9, Qt_5_11 = Qt_5_10, Qt_5_12 = 18, -#if QT_VERSION >= 0x050d00 + Qt_5_13 = 19, +#if QT_VERSION >= 0x050e00 #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif - Qt_DefaultCompiledVersion = Qt_5_12 + Qt_DefaultCompiledVersion = Qt_5_13 }; enum ByteOrder { @@ -130,7 +131,10 @@ public: QIODevice *device() const; void setDevice(QIODevice *); +#if QT_DEPRECATED_SINCE(5, 13) + QT_DEPRECATED_X("Use QDataStream::setDevice(nullptr) instead") void unsetDevice(); +#endif bool atEnd() const; diff --git a/src/corelib/serialization/qjson_p.h b/src/corelib/serialization/qjson_p.h index feba1faac6..40b2414e4a 100644 --- a/src/corelib/serialization/qjson_p.h +++ b/src/corelib/serialization/qjson_p.h @@ -746,7 +746,7 @@ public: bool valid() const; private: - Q_DISABLE_COPY(Data) + Q_DISABLE_COPY_MOVE(Data) }; } diff --git a/src/corelib/serialization/qjsonarray.cpp b/src/corelib/serialization/qjsonarray.cpp index 1187bb03a3..7dfa9b43f0 100644 --- a/src/corelib/serialization/qjsonarray.cpp +++ b/src/corelib/serialization/qjsonarray.cpp @@ -675,6 +675,14 @@ bool QJsonArray::operator!=(const QJsonArray &other) const \sa begin(), constEnd() */ +/*! \fn QJsonArray::const_iterator QJsonArray::cbegin() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item + in the array. + + \sa begin(), cend() +*/ + /*! \fn QJsonArray::iterator QJsonArray::end() Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item @@ -696,6 +704,14 @@ bool QJsonArray::operator!=(const QJsonArray &other) const \sa constBegin(), end() */ +/*! \fn QJsonArray::const_iterator QJsonArray::cend() const + + Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary + item after the last item in the array. + + \sa cbegin(), end() +*/ + /*! \fn void QJsonArray::push_back(const QJsonValue &value) This function is provided for STL compatibility. It is equivalent @@ -1259,5 +1275,22 @@ QDebug operator<<(QDebug dbg, const QJsonArray &a) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QJsonArray &array) +{ + QJsonDocument doc{array}; + stream << doc.toJson(QJsonDocument::Compact); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QJsonArray &array) +{ + QJsonDocument doc; + stream >> doc; + array = doc.array(); + return stream; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonarray.h b/src/corelib/serialization/qjsonarray.h index 5dff4a0aa9..ba346fb848 100644 --- a/src/corelib/serialization/qjsonarray.h +++ b/src/corelib/serialization/qjsonarray.h @@ -214,9 +214,11 @@ public: inline iterator begin() { detach2(); return iterator(this, 0); } inline const_iterator begin() const { return const_iterator(this, 0); } inline const_iterator constBegin() const { return const_iterator(this, 0); } + inline const_iterator cbegin() const { return const_iterator(this, 0); } inline iterator end() { detach2(); return iterator(this, size()); } inline const_iterator end() const { return const_iterator(this, size()); } inline const_iterator constEnd() const { return const_iterator(this, size()); } + inline const_iterator cend() const { return const_iterator(this, size()); } iterator insert(iterator before, const QJsonValue &value) { insert(before.i, value); return before; } iterator erase(iterator it) { removeAt(it.i); return it; } @@ -271,6 +273,11 @@ Q_CORE_EXPORT uint qHash(const QJsonArray &array, uint seed = 0); Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonArray &); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonArray &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QJsonArray &); +#endif + QT_END_NAMESPACE #endif // QJSONARRAY_H diff --git a/src/corelib/serialization/qjsoncbor.cpp b/src/corelib/serialization/qjsoncbor.cpp index 4f756df97c..dc5f384108 100644 --- a/src/corelib/serialization/qjsoncbor.cpp +++ b/src/corelib/serialization/qjsoncbor.cpp @@ -824,7 +824,7 @@ QCborArray QCborArray::fromJsonArray(const QJsonArray &array) { QCborArray a; a.detach(array.size()); - for (const QJsonValue v : array) { + for (const QJsonValue &v : array) { if (v.isString()) a.d->append(v.toString()); else diff --git a/src/corelib/serialization/qjsondocument.cpp b/src/corelib/serialization/qjsondocument.cpp index 0cd86d3ded..179a87c699 100644 --- a/src/corelib/serialization/qjsondocument.cpp +++ b/src/corelib/serialization/qjsondocument.cpp @@ -47,6 +47,7 @@ #include "qjsonwriter_p.h" #include "qjsonparser_p.h" #include "qjson_p.h" +#include "qdatastream.h" QT_BEGIN_NAMESPACE @@ -657,4 +658,23 @@ QDebug operator<<(QDebug dbg, const QJsonDocument &o) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QJsonDocument &doc) +{ + stream << doc.toJson(QJsonDocument::Compact); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QJsonDocument &doc) +{ + QByteArray buffer; + stream >> buffer; + QJsonParseError parseError{}; + doc = QJsonDocument::fromJson(buffer, &parseError); + if (parseError.error && !buffer.isEmpty()) + stream.setStatus(QDataStream::ReadCorruptData); + return stream; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsondocument.h b/src/corelib/serialization/qjsondocument.h index b784890c54..a749439b7d 100644 --- a/src/corelib/serialization/qjsondocument.h +++ b/src/corelib/serialization/qjsondocument.h @@ -172,6 +172,11 @@ Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonDocument) Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonDocument &); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonDocument &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QJsonDocument &); +#endif + QT_END_NAMESPACE #endif // QJSONDOCUMENT_H diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp index 950bec535b..a9f25a119c 100644 --- a/src/corelib/serialization/qjsonobject.cpp +++ b/src/corelib/serialization/qjsonobject.cpp @@ -1320,4 +1320,21 @@ QDebug operator<<(QDebug dbg, const QJsonObject &o) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QJsonObject &object) +{ + QJsonDocument doc{object}; + stream << doc.toJson(QJsonDocument::Compact); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QJsonObject &object) +{ + QJsonDocument doc; + stream >> doc; + object = doc.object(); + return stream; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonobject.h b/src/corelib/serialization/qjsonobject.h index be42d3747a..80fe6b2f3f 100644 --- a/src/corelib/serialization/qjsonobject.h +++ b/src/corelib/serialization/qjsonobject.h @@ -268,6 +268,11 @@ Q_CORE_EXPORT uint qHash(const QJsonObject &object, uint seed = 0); Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonObject &); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonObject &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QJsonObject &); +#endif + QT_END_NAMESPACE #endif // QJSONOBJECT_H diff --git a/src/corelib/serialization/qjsonvalue.cpp b/src/corelib/serialization/qjsonvalue.cpp index 2c04da4885..1fc610d7c7 100644 --- a/src/corelib/serialization/qjsonvalue.cpp +++ b/src/corelib/serialization/qjsonvalue.cpp @@ -45,6 +45,7 @@ #include <qvariant.h> #include <qstringlist.h> #include <qdebug.h> +#include "qdatastream.h" #ifndef QT_BOOTSTRAPPED # include <qcborarray.h> @@ -933,4 +934,78 @@ QDebug operator<<(QDebug dbg, const QJsonValue &o) } #endif +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QJsonValue &v) +{ + quint8 type = v.t; + stream << type; + switch (type) { + case QJsonValue::Undefined: + case QJsonValue::Null: + break; + case QJsonValue::Bool: + stream << v.toBool(); + break; + case QJsonValue::Double: + stream << v.toDouble(); + break; + case QJsonValue::String: + stream << v.toString(); + break; + case QJsonValue::Array: + stream << v.toArray(); + break; + case QJsonValue::Object: + stream << v.toObject(); + break; + } + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QJsonValue &v) +{ + quint8 type; + stream >> type; + switch (type) { + case QJsonValue::Undefined: + case QJsonValue::Null: + v = QJsonValue{QJsonValue::Type(type)}; + break; + case QJsonValue::Bool: { + bool b; + stream >> b; + v = QJsonValue(b); + break; + } case QJsonValue::Double: { + double d; + stream >> d; + v = QJsonValue{d}; + break; + } case QJsonValue::String: { + QString s; + stream >> s; + v = QJsonValue{s}; + break; + } + case QJsonValue::Array: { + QJsonArray a; + stream >> a; + v = QJsonValue{a}; + break; + } + case QJsonValue::Object: { + QJsonObject o; + stream >> o; + v = QJsonValue{o}; + break; + } + default: { + stream.setStatus(QDataStream::ReadCorruptData); + v = QJsonValue{QJsonValue::Undefined}; + } + } + return stream; +} +#endif + QT_END_NAMESPACE diff --git a/src/corelib/serialization/qjsonvalue.h b/src/corelib/serialization/qjsonvalue.h index d8e121524d..0339eb59f7 100644 --- a/src/corelib/serialization/qjsonvalue.h +++ b/src/corelib/serialization/qjsonvalue.h @@ -152,6 +152,7 @@ private: friend class QJsonObject; friend class QCborValue; 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); void stringDataFromQStringHelper(const QString &string); @@ -218,7 +219,6 @@ private: uint index : 31; }; -#ifndef Q_QDOC // ### Qt 6: Get rid of these fake pointer classes class QJsonValuePtr { @@ -243,7 +243,6 @@ public: QJsonValueRef& operator*() { return valueRef; } QJsonValueRef* operator->() { return &valueRef; } }; -#endif Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QJsonValue) @@ -253,6 +252,11 @@ Q_CORE_EXPORT uint qHash(const QJsonValue &value, uint seed = 0); Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); #endif +#ifndef QT_NO_DATASTREAM +Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QJsonValue &); +Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QJsonValue &); +#endif + QT_END_NAMESPACE #endif // QJSONVALUE_H diff --git a/src/corelib/serialization/qtextstream.cpp b/src/corelib/serialization/qtextstream.cpp index fb7b677b2d..c9ba183a50 100644 --- a/src/corelib/serialization/qtextstream.cpp +++ b/src/corelib/serialization/qtextstream.cpp @@ -1332,7 +1332,7 @@ void QTextStream::setDevice(QIODevice *device) /*! Returns the current device associated with the QTextStream, - or 0 if no device has been assigned. + or \nullptr if no device has been assigned. \sa setDevice(), string() */ @@ -1369,8 +1369,8 @@ void QTextStream::setString(QString *string, QIODevice::OpenMode openMode) } /*! - Returns the current string assigned to the QTextStream, or 0 if no - string has been assigned. + Returns the current string assigned to the QTextStream, or + \nullptr if no string has been assigned. \sa setString(), device() */ diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index 827996ee2d..0170be7602 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -223,7 +223,7 @@ QString QXmlStreamReaderPrivate::resolveUndeclaredEntity(const QString &name) The stream reader does \e not take ownership of the resolver. It's the callers responsibility to ensure that the resolver is valid during the entire life-time of the stream reader object, or until - another resolver or 0 is set. + another resolver or \nullptr is set. \sa entityResolver() */ @@ -236,7 +236,7 @@ void QXmlStreamReader::setEntityResolver(QXmlStreamEntityResolver *resolver) /*! \since 4.4 - Returns the entity resolver, or 0 if there is no entity resolver. + Returns the entity resolver, or \nullptr if there is no entity resolver. \sa setEntityResolver() */ @@ -480,7 +480,7 @@ void QXmlStreamReader::setDevice(QIODevice *device) /*! Returns the current device associated with the QXmlStreamReader, - or 0 if no device has been assigned. + or \nullptr if no device has been assigned. \sa setDevice() */ @@ -3315,7 +3315,7 @@ void QXmlStreamWriter::setDevice(QIODevice *device) /*! Returns the current device associated with the QXmlStreamWriter, - or 0 if no device has been assigned. + or \nullptr if no device has been assigned. \sa setDevice() */ diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g index 10bfcd491c..e6328a11ac 100644 --- a/src/corelib/serialization/qxmlstream.g +++ b/src/corelib/serialization/qxmlstream.g @@ -41,6 +41,8 @@ %merged_output qxmlstream_p.h +%expect 4 + %token NOTOKEN %token SPACE " " %token LANGLE "<" @@ -144,7 +146,12 @@ %start document + + /. + +#include <QtCore/private/qglobal_p.h> + template <typename T> class QXmlStreamSimpleStack { T *data; int tos, cap; @@ -155,7 +162,8 @@ public: inline void reserve(int extraCapacity) { if (tos + extraCapacity + 1 > cap) { cap = qMax(tos + extraCapacity + 1, cap << 1 ); - data = reinterpret_cast<T *>(realloc(data, cap * sizeof(T))); + void *ptr = realloc(static_cast<void *>(data), cap * sizeof(T)); + data = reinterpret_cast<T *>(ptr); Q_CHECK_PTR(data); } } @@ -753,7 +761,7 @@ bool QXmlStreamReaderPrivate::parse() state_stack[tos] = 0; return true; } else if (act > 0) { - if (++tos == stack_size-1) + if (++tos >= stack_size-1) reallocateStack(); Value &val = sym_stack[tos]; @@ -890,7 +898,7 @@ doctype_decl ::= langle_bang DOCTYPE qname markup space_opt RANGLE; /. case $rule_number: dtdName = symString(3); - // fall through + Q_FALLTHROUGH(); ./ doctype_decl ::= doctype_decl_start external_id space_opt markup space_opt RANGLE; /. |