diff options
author | Thiago Macieira <thiago.macieira@intel.com> | 2017-12-12 23:34:08 -0800 |
---|---|---|
committer | Liang Qi <liang.qi@qt.io> | 2018-06-08 07:46:29 +0000 |
commit | 92e472302a0ef8390f60fd91cac7f360b199a1e4 (patch) | |
tree | f35dc12c20bff0c952e81f1c53d5f2c55494a4ed /src/corelib/serialization | |
parent | 95e6ab332917c83e874442eca4785fe3d62cec9b (diff) |
Long live DOM API for CBOR!
This is very similar to QJsonDocument, but there's no QCborDocument.
QCborValue is that.
[ChangeLog][QtCore] Added QCborValue, QCborArray and QCborMap, classes
that permit DOM-like access to CBOR data. The API is similar to
QJsonValue, QJsonArray and QJsonObject, respectively.
Change-Id: I9741f017961b410c910dfffd14ffca50dd8ef3ba
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src/corelib/serialization')
-rw-r--r-- | src/corelib/serialization/qcborarray.cpp | 1141 | ||||
-rw-r--r-- | src/corelib/serialization/qcborarray.h | 272 | ||||
-rw-r--r-- | src/corelib/serialization/qcbormap.cpp | 1553 | ||||
-rw-r--r-- | src/corelib/serialization/qcbormap.h | 320 | ||||
-rw-r--r-- | src/corelib/serialization/qcborstream.h | 1 | ||||
-rw-r--r-- | src/corelib/serialization/qcborvalue.cpp | 2550 | ||||
-rw-r--r-- | src/corelib/serialization/qcborvalue.h | 427 | ||||
-rw-r--r-- | src/corelib/serialization/qcborvalue_p.h | 366 | ||||
-rw-r--r-- | src/corelib/serialization/serialization.pri | 9 |
9 files changed, 6639 insertions, 0 deletions
diff --git a/src/corelib/serialization/qcborarray.cpp b/src/corelib/serialization/qcborarray.cpp new file mode 100644 index 0000000000..71a1ad2e9a --- /dev/null +++ b/src/corelib/serialization/qcborarray.cpp @@ -0,0 +1,1141 @@ +/**************************************************************************** +** +** 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 "qcborarray.h" +#include "qcborvalue_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QCborArray + \inmodule QtCore + \ingroup cbor + \reentrant + \since 5.12 + + \brief The QCborArray class is used to hold an array of CBOR elements. + + This class can be used to hold one sequential container in CBOR (an array). + CBOR is the Concise Binary Object Representation, a very compact form of + binary data encoding that is a superset of 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}. + + QCborArray is very similar to \l QVariantList and \l QJsonArray and its API + is almost identical to those two classes. It can also be converted to and + from those two, though there may be loss of information in some + conversions. + + \sa QCborValue, QCborMap, QJsonArray, QList, QVector + */ + +/*! + \typedef QCborArray::size_type + + A typedef to qsizetype. + */ + +/*! + \typedef QCborArray::difference_type + + A typedef to qsizetype. + */ + +/*! + \typedef QCborArray::value_type + + The type of values that can be held in a QCborArray: that is, QCborValue. + */ + +/*! + \typedef QCborArray::pointer + + A typedef to \c{QCborValue *}, for compatibility with generic algorithms. + */ + +/*! + \typedef QCborArray::const_pointer + + A typedef to \c{const QCborValue *}, for compatibility with generic algorithms. + */ + +/*! + \typedef QCborArray::reference + + A typedef to \c{QCborValue &}, for compatibility with generic algorithms. + */ + +/*! + \typedef QCborArray::const_reference + + A typedef to \c{const QCborValue &}, for compatibility with generic algorithms. + */ + +/*! + Constructs an empty QCborArray. + */ +QCborArray::QCborArray() Q_DECL_NOTHROW + : d(nullptr) +{ +} + +/*! + Copies the contents of \a other into this object. + */ +QCborArray::QCborArray(const QCborArray &other) Q_DECL_NOTHROW + : d(other.d) +{ +} + +/*! + \fn QCborArray::QCborArray(std::initializer_list<QCborValue> args) + + Initializes this QCborArray from the C++ brace-enclosed list found in \a + args, as in the following example: + + \code + QCborArray a = { null, 0, 1, 1.5, 2, "Hello", QByteArray("World") }; + \endcode + */ + +/*! + Destroys this QCborArray and frees any associated resources. + */ +QCborArray::~QCborArray() +{ +} + +/*! + Replaces the contents of this array with that found in \a other, then + returns a reference to this object. + */ +QCborArray &QCborArray::operator=(const QCborArray &other) Q_DECL_NOTHROW +{ + d = other.d; + return *this; +} + +/*! + \fn void QCborArray::swap(QCborArray &other) + + Swaps the contents of this object and \a other. + */ + +/*! + \fn QCborValue QCborArray::toCborValue() const + + Explicitly construcuts a \l QCborValue object that represents this array. + This function is usually not necessary since QCborValue has a constructor + for QCborArray, so the conversion is implicit. + + Converting QCborArray to QCborValue allows it to be used in any context + where QCborValues can be used, including as items in QCborArrays and as keys + and mapped types in QCborMap. Converting an array to QCborValue allows + access to QCborValue::toCbor(). + + \sa QCborValue::QCborValue(const QCborArray &) + */ + +/*! + Returns the size of this array. + + \sa isEmpty() + */ +qsizetype QCborArray::size() const Q_DECL_NOTHROW +{ + return d ? d->elements.size() : 0; +} + +/*! + \fn bool QCborArray::isEmpty() const + + Returns true if this QCborArray is empty (that is if size() is 0). + + \sa size() + */ + +/*! + Returns the QCborValue element at position \a i in the array. + + If the array is smaller than \a i elements, this function returns a + QCborValue containing an undefined value. For that reason, it is not + possible with this function to tell apart the situation where the array is + not large enough from the case where the array starts with an undefined + value. + + \sa operator[](), first(), last(), insert(), prepend(), append(), + removeAt(), takeAt() + */ +QCborValue QCborArray::at(qsizetype i) const +{ + if (!d || size_t(i) >= size_t(size())) + return QCborValue(); + return d->valueAt(i); +} + +/*! + \fn QCborValue QCborArray::first() const + + Returns the first QCborValue of this array. + + If the array is empty, this function returns a QCborValue containing an + undefined value. For that reason, it is not possible with this function to + tell apart the situation where the array is not large enough from the case + where the array ends with an undefined value. + + \sa operator[](), at(), last(), insert(), prepend(), append(), + removeAt(), takeAt() + */ + +/*! + \fn QCborValue QCborArray::last() const + + Returns the last QCborValue of this array. + + If the array is empty, this function returns a QCborValue containing an + undefined value. For that reason, it is not possible with this function to + tell apart the situation where the array is not large enough from the case + where the array ends with an undefined value. + + \sa operator[](), at(), first(), insert(), prepend(), append(), + removeAt(), takeAt() + */ + +/*! + \fn QCborValue QCborArray::operator[](qsizetype i) const + + Returns the QCborValue element at position \a i in the array. + + If the array is smaller than \a i elements, this function returns a + QCborValue containing an undefined value. For that reason, it is not + possible with this function to tell apart the situation where the array is + not large enough from the case where the array contains an undefined value + at position \a i. + + \sa at(), first(), last(), insert(), prepend(), append(), + removeAt(), takeAt() + */ + +/*! + \fn QCborValueRef QCborArray::first() + + Returns a reference to the first QCborValue of this array. The array must + not be empty. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + \sa operator[](), at(), last(), insert(), prepend(), append(), + removeAt(), takeAt() + */ + +/*! + \fn QCborValueRef QCborArray::last() + + Returns a reference to the last QCborValue of this array. The array must + not be empty. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + \sa operator[](), at(), first(), insert(), prepend(), append(), + removeAt(), takeAt() + */ + +/*! + \fn QCborValueRef QCborArray::operator[](qsizetype i) + + Returns a reference to the QCborValue element at position \a i in the + array. The array must have at least \a i elements. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + \sa at(), first(), last(), insert(), prepend(), append(), + removeAt(), takeAt() + */ + +/*! + Inserts \a value into the array at position \a i in this array. The array + must have at least \a i elements before the insertion. + + \sa at(), operator[](), first(), last(), prepend(), append(), + removeAt(), takeAt() + */ +void QCborArray::insert(qsizetype i, const QCborValue &value) +{ + Q_ASSERT(size_t(i) <= size_t(size()) || i == -1); + if (i < 0) + i = size(); + detach(qMax(i + 1, size())); + d->insertAt(i, value); +} + +/*! + \fn void QCborArray::prepend(const QCborValue &value) + + Prepends \a value into the array before any other elements it may already + contain. + + \sa at(), operator[](), first(), last(), insert(), append(), + removeAt(), takeAt() + */ + +/*! + \fn void QCborArray::append(const QCborValue &value) + + Appends \a value into the array after all other elements it may already + contain. + + \sa at(), operator[](), first(), last(), insert(), prepend(), + removeAt(), takeAt() + */ + +/*! + Removes the item at position \a i from the array. The array must have more + than \a i elements before the removal. + + \sa takeAt(), removeFirst(), removeLast(), at(), operator[](), insert(), + prepend(), append() + */ +void QCborArray::removeAt(qsizetype i) +{ + detach(size()); + d->removeAt(i); +} + +/*! + \fn QCborValue QCborArray::takeAt(qsizetype i) + + Removes the item at position \a i from the array and returns it. The array + must have more than \a i elements before the removal. + + \sa removeAt(), removeFirst(), removeLast(), at(), operator[](), insert(), + prepend(), append() + */ + +/*! + \fn void QCborArray::removeFirst() + + Removes the first item in the array, making the second element become the + first. The array must not be empty before this call. + + \sa removeAt(), takeFirst(), removeLast(), at(), operator[](), insert(), + prepend(), append() + */ + +/*! + \fn void QCborArray::removeLast() + + Removes the last item in the array. The array must not be empty before this + call. + + \sa removeAt(), takeLast(), removeFirst(), at(), operator[](), insert(), + prepend(), append() + */ + +/*! + \fn void QCborArray::takeFirst() + + Removes the first item in the array and returns it, making the second + element become the first. The array must not be empty before this call. + + \sa takeAt(), removeFirst(), removeLast(), at(), operator[](), insert(), + prepend(), append() + */ + +/*! + \fn void QCborArray::takeLast() + + Removes the last item in the array and returns it. The array must not be + empty before this call. + + \sa takeAt(), removeLast(), removeFirst(), at(), operator[](), insert(), + prepend(), append() + */ + +/*! + Returns true if this array contains an element that is equal to \a value. + */ +bool QCborArray::contains(const QCborValue &value) const +{ + for (qsizetype i = 0; i < size(); ++i) { + int cmp = d->compareElement(i, value); + if (cmp == 0) + return true; + } + return false; +} + +/*! + \fn int QCborArray::compare(const QCborArray &other) const + + Compares this array and \a other, comparing each element in sequence, and + returns an integer that indicates whether this array should be sorted + before (if the result is negative) or after \a other (if the result is + positive). If this function returns 0, the two arrays are equal and contain + the same elements. + + For more information on CBOR sorting order, see QCborValue::compare(). + + \sa QCborValue::compare(), QCborMap::compare(), operator==() + */ + +/*! + \fn bool QCborArray::operator==(const QCborArray &other) const + + Compares this array and \a other, comparing each element in sequence, and + returns true if both arrays contains the same elements, false otherwise. + + For more information on CBOR equality in Qt, see, QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator!=(), operator<() + */ + +/*! + \fn bool QCborArray::operator!=(const QCborArray &other) const + + Compares this array and \a other, comparing each element in sequence, and + returns true if the two arrays' contents are different, false otherwise. + + For more information on CBOR equality in Qt, see, QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator==(), operator<() + */ + +/*! + \fn bool QCborArray::operator<(const QCborArray &other) const + + Compares this array and \a other, comparing each element in sequence, and + returns true if this array should be sorted before \a other, false + otherwise. + + For more information on CBOR sorting order, see QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator==(), operator!=() + */ + +/*! + \typedef QCborArray::iterator + + A synonym to QCborArray::Iterator. + */ + +/*! + \typedef QCborArray::const_iterator + + A synonym to QCborArray::ConstIterator. + */ + +/*! + \fn QCborArray::iterator QCborArray::begin() + + Returns an array iterator pointing to the first item in this array. If the + array is empty, then this function returns the same as end(). + + \sa constBegin(), end() + */ + +/*! + \fn QCborArray::const_iterator QCborArray::begin() const + + Returns an array iterator pointing to the first item in this array. If the + array is empty, then this function returns the same as end(). + + \sa constBegin(), constEnd() + */ + +/*! + \fn QCborArray::const_iterator QCborArray::cbegin() const + + Returns an array iterator pointing to the first item in this array. If the + array is empty, then this function returns the same as end(). + + \sa constBegin(), constEnd() + */ + +/*! + \fn QCborArray::const_iterator QCborArray::constBegin() const + + Returns an array iterator pointing to the first item in this array. If the + array is empty, then this function returns the same as end(). + + \sa begin(), constEnd() + */ + +/*! + \fn QCborArray::iterator QCborArray::end() + + Returns an array iterator pointing to just after the last element in this + array. + + \sa begin(), constEnd() + */ + +/*! + \fn QCborArray::const_iterator QCborArray::end() const + + Returns an array iterator pointing to just after the last element in this + array. + + \sa constBegin(), constEnd() + */ + +/*! + \fn QCborArray::const_iterator QCborArray::cend() const + + Returns an array iterator pointing to just after the last element in this + array. + + \sa constBegin(), constEnd() + */ + +/*! + \fn QCborArray::const_iterator QCborArray::constEnd() const + + Returns an array iterator pointing to just after the last element in this + array. + + \sa constBegin(), end() + */ + +/*! + \fn QCborArray::iterator QCborArray::insert(iterator before, const QCborValue &value) + \overload + + Inserts \a value into this array before element \a before and returns an + array iterator pointing to the just-inserted element. + + \sa erase(), removeAt(), prepend(), append() + */ + +/*! + \fn QCborArray::iterator QCborArray::erase(iterator it) + + Removes the element pointed to by the array iterator \a it from this array, + then returns an iterator to the next element (the one that took the same + position in the array that \a it used to occupy). + + \sa insert(), removeAt(), takeAt(), takeFirst(), takeLast() + */ + +/*! + \fn void QCborArray::push_back(const QCborValue &t) + + Synonym for append(). This function is provided for compatibility with + generic code that uses the Standard Library API. + + Appends the element \a t to this array. + + \sa append(), push_front(), pop_back(), prepend(), insert() + */ + +/*! + \fn void QCborArray::push_front(const QCborValue &t) + + Synonym for prepend(). This function is provided for compatibility with + generic code that uses the Standard Library API. + + Prepends the element \a t to this array. + + \sa prepend(), push_back(), pop_front(), append(), insert() + */ + +/*! + \fn void QCborArray::pop_front() + + Synonym for removeFirst(). This function is provided for compatibility with + generic code that uses the Standard Library API. + + Removes the first element of this array. The array must not be empty before + the removal + + \sa removeFirst(), takeFirst(), pop_back(), push_front(), prepend(), insert() + */ + +/*! + \fn void QCborArray::pop_back() + + Synonym for removeLast(). This function is provided for compatibility with + generic code that uses the Standard Library API. + + Removes the last element of this array. The array must not be empty before + the removal + + \sa removeLast(), takeLast(), pop_front(), push_back(), append(), insert() + */ + +/*! + \fn bool QCborArray::empty() const + + Synonym for isEmpty(). This function is provided for compatibility with + generic code that uses the Standard Library API. + + Returns true if this array is empty (size() == 0). + + \sa isEmpty(), size() + */ + +/*! + \fn QCborArray QCborArray::operator+(const QCborValue &v) const + + Returns a new QCborArray containing the same elements as this array, plus + \a v appended as the last element. + + \sa operator+=(), operator<<(), append() + */ + +/*! + \fn QCborArray &QCborArray::operator+=(const QCborValue &v) + + Appends \a v to this array and returns a reference to this array. + + \sa append(), insert(), operator+(), operator<<() + */ + +/*! + \fn QCborArray &QCborArray::operator<<(const QCborValue &v) + + Appends \a v to this array and returns a reference to this array. + + \sa append(), insert(), operator+(), operator+=() + */ + +void QCborArray::detach(qsizetype reserved) +{ + d = QCborContainerPrivate::detach(d.data(), reserved ? reserved : size()); +} + +/*! + \class QCborArray::Iterator + \inmodule QtCore + \ingroup cbor + \since 5.12 + + \brief The QCborArray::Iterator class provides an STL-style non-const iterator for QCborArray. + + QCborArray::Iterator allows you to iterate over a QCborArray and to modify + the array item associated with the iterator. If you want to iterate over a + const QCborArray, use QCborArray::ConstIterator instead. It is generally a + good practice to use QCborArray::ConstIterator on a non-const QCborArray as + well, unless you need to change the QCborArray through the iterator. Const + iterators are slightly faster and improve code readability. + + Iterators are initialized by using a QCborArray function like + QCborArray::begin(), QCborArray::end(), or QCborArray::insert(). Iteration + is only possible after that. + + Most QCborArray functions accept an integer index rather than an iterator. + For that reason, iterators are rarely useful in connection with QCborArray. + One place where STL-style iterators do make sense is as arguments to + \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be aware that + any non-const function call performed on the QCborArray will render all + existing iterators undefined. + + \sa QCborArray::ConstIterator +*/ + +/*! + \typedef QCborArray::Iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating this iterator + is a random access iterator. + */ + +/*! + \typedef QCborArray::Iterator::difference_type + \internal +*/ + +/*! + \typedef QCborArray::Iterator::value_type + \internal +*/ + +/*! + \typedef QCborArray::Iterator::reference + \internal +*/ + +/*! + \typedef QCborArray::Iterator::pointer + \internal +*/ + +/*! + \fn QCborArray::Iterator::Iterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called on an + uninitialized iterator. Use operator=() to assign a value to it before + using it. + + \sa QCborArray::begin(), QCborArray::end() +*/ + +/*! + \fn QCborArray::Iterator::Iterator(const Iterator &other) + + Makes a copy of \a other. + */ + +/*! + \fn QCborArray::Iterator &QCborArray::Iterator::operator=(const Iterator &other) + + Makes this iterator a copy of \a other and returns a reference to this iterator. + */ + +/*! + \fn QCborValueRef QCborArray::Iterator::operator*() const + + Returns a modifiable reference to the current item. + + You can change the value of an item by using operator*() on the left side + of an assignment. + + The return value is of type QCborValueRef, a helper class for QCborArray + and QCborMap. When you get an object of type QCborValueRef, you can use it + as if it were a reference to a QCborValue. If you assign to it, the + assignment will apply to the element in the QCborArray or QCborMap from + which you got the reference. +*/ + +/*! + \fn QCborValueRef *QCborArray::Iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + +/*! + \fn QCborValueRef QCborArray::Iterator::operator[](qsizetype j) const + + Returns a modifiable reference to the item at a position \a j steps forward + from the item pointed to by this iterator. + + This function is provided to make QCborArray iterators behave like C++ + pointers. + + The return value is of type QCborValueRef, a helper class for QCborArray + and QCborMap. When you get an object of type QCborValueRef, you can use it + as if it were a reference to a QCborValue. If you assign to it, the + assignment will apply to the element in the QCborArray or QCborMap from + which you got the reference. + + \sa operator+() +*/ + +/*! + \fn bool QCborArray::Iterator::operator==(const Iterator &other) const + \fn bool QCborArray::Iterator::operator==(const ConstIterator &other) const + + Returns \c true if \a other points to the same entry in the array as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QCborArray::Iterator::operator!=(const Iterator &other) const + \fn bool QCborArray::Iterator::operator!=(const ConstIterator &other) const + + Returns \c true if \a other points to a different entry in the array than + this iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool QCborArray::Iterator::operator<(const Iterator& other) const + \fn bool QCborArray::Iterator::operator<(const ConstIterator& other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs before the entry pointed to by the \a other iterator. +*/ + +/*! + \fn bool QCborArray::Iterator::operator<=(const Iterator& other) const + \fn bool QCborArray::Iterator::operator<=(const ConstIterator& other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs before or is the same entry as is pointed to by the \a other + iterator. +*/ + +/*! + \fn bool QCborArray::Iterator::operator>(const Iterator& other) const + \fn bool QCborArray::Iterator::operator>(const ConstIterator& other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs after the entry pointed to by the \a other iterator. + */ + +/*! + \fn bool QCborArray::Iterator::operator>=(const Iterator& other) const + \fn bool QCborArray::Iterator::operator>=(const ConstIterator& other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs after or is the same entry as is pointed to by the \a other + iterator. +*/ + +/*! + \fn QCborArray::Iterator &QCborArray::Iterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the next item in + the array and returns this iterator. + + Calling this function on QCborArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! + \fn QCborArray::Iterator QCborArray::Iterator::operator++(int) + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the next item + in the array and returns an iterator to the previously current item. +*/ + +/*! + \fn QCborArray::Iterator &QCborArray::Iterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item current and + returns this iterator. + + Calling this function on QCborArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! + \fn QCborArray::Iterator QCborArray::Iterator::operator--(int) + \overload + + The postfix -- operator, \c{it--}, makes the preceding item current and + returns an iterator to the previously current item. +*/ + +/*! + \fn QCborArray::Iterator &QCborArray::Iterator::operator+=(qsizetype j) + + Advances the iterator by \a j positions. If \a j is negative, the iterator + goes backward. Returns a reference to this iterator. + + \sa operator-=(), operator+() +*/ + +/*! + \fn QCborArray::Iterator &QCborArray::Iterator::operator-=(qsizetype j) + + Makes the iterator go back by \a j positions. If \a j is negative, the + iterator goes forward. Returns a reference to this iterator. + + \sa operator+=(), operator-() +*/ + +/*! + \fn QCborArray::Iterator QCborArray::Iterator::operator+(qsizetype j) const + + Returns an iterator to the item at position \a j steps forward from this + iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! + \fn QCborArray::Iterator QCborArray::Iterator::operator-(qsizetype j) const + + Returns an iterator to the item at position \a j steps backward from this + iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! + \fn qsizetype QCborArray::Iterator::operator-(Iterator other) const + + Returns the offset of this iterator relative to \a other. +*/ + +/*! + \class QCborArray::ConstIterator + \inmodule QtCore + \ingroup cbor + \since 5.12 + + \brief The QCborArray::ConstIterator class provides an STL-style const iterator for QCborArray. + + QCborArray::ConstIterator allows you to iterate over a QCborArray. If you + want to modify the QCborArray as you iterate over it, use + QCborArray::Iterator instead. It is generally good practice to use + QCborArray::ConstIterator, even on a non-const QCborArray, when you don't + need to change the QCborArray through the iterator. Const iterators are + slightly faster and improves code readability. + + Iterators are initialized by using a QCborArray function like + QCborArray::begin() or QCborArray::end(). Iteration is only possible after + that. + + Most QCborArray functions accept an integer index rather than an iterator. + For that reason, iterators are rarely useful in connection with QCborArray. + One place where STL-style iterators do make sense is as arguments to + \l{generic algorithms}. + + Multiple iterators can be used on the same array. However, be aware that + any non-const function call performed on the QCborArray will render all + existing iterators undefined. + + \sa QCborArray::Iterator +*/ + +/*! + \fn QCborArray::ConstIterator::ConstIterator() + + Constructs an uninitialized iterator. + + Functions like operator*() and operator++() should not be called on an + uninitialized iterator. Use operator=() to assign a value to it before + using it. + + \sa QCborArray::constBegin(), QCborArray::constEnd() +*/ + +/*! + \fn QCborArray::ConstIterator &QCborArray::ConstIterator::operator=(const ConstIterator &other) + + Makes this iterator a copy of \a other and returns a reference to this iterator. +*/ + +/*! + \typedef QCborArray::ConstIterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating this iterator + is a random access iterator. +*/ + +/*! + \typedef QCborArray::ConstIterator::difference_type + \internal +*/ + +/*! + \typedef QCborArray::ConstIterator::value_type + \internal +*/ + +/*! + \typedef QCborArray::ConstIterator::reference + \internal +*/ + +/*! + \typedef QCborArray::ConstIterator::pointer + \internal +*/ + +/*! + \fn QCborArray::ConstIterator::ConstIterator(const ConstIterator &other) + + Constructs a copy of \a other. +*/ + +/*! + \fn QCborValue QCborArray::ConstIterator::operator*() const + + Returns the current item. +*/ + +/*! + \fn const QCborValue *QCborArray::ConstIterator::operator->() const + + Returns a pointer to the current item. +*/ + +/*! + \fn QCborValue QCborArray::ConstIterator::operator[](qsizetype j) const + + Returns the item at a position \a j steps forward from the item pointed to + by this iterator. + + This function is provided to make QCborArray iterators behave like C++ + pointers. + + \sa operator+() +*/ + +/*! + \fn bool QCborArray::ConstIterator::operator==(const ConstIterator &other) const + + Returns \c true if \a other points to the same entry in the array as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QCborArray::ConstIterator::operator!=(const ConstIterator &other) const + + Returns \c true if \a other points to a different entry in the array than + this iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn bool QCborArray::ConstIterator::operator<(const ConstIterator &other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs before the entry pointed to by the \a other iterator. +*/ + +/*! + \fn bool QCborArray::ConstIterator::operator<=(const ConstIterator &other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs before or is the same entry as is pointed to by the \a other + iterator. +*/ + +/*! + \fn bool QCborArray::ConstIterator::operator>(const ConstIterator &other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs after the entry pointed to by the \a other iterator. +*/ + +/*! + \fn bool QCborArray::ConstIterator::operator>=(const ConstIterator &other) const + + Returns \c true if the entry in the array pointed to by this iterator + occurs after or is the same entry as is pointed to by the \a other + iterator. +*/ + +/*! + \fn QCborArray::ConstIterator &QCborArray::ConstIterator::operator++() + + The prefix ++ operator, \c{++it}, advances the iterator to the next item in + the array and returns this iterator. + + Calling this function on QCborArray::end() leads to undefined results. + + \sa operator--() +*/ + +/*! + \fn QCborArray::ConstIterator QCborArray::ConstIterator::operator++(int) + \overload + + The postfix ++ operator, \c{it++}, advances the iterator to the next item + in the array and returns an iterator to the previously current item. +*/ + +/*! + \fn QCborArray::ConstIterator &QCborArray::ConstIterator::operator--() + + The prefix -- operator, \c{--it}, makes the preceding item current and + returns this iterator. + + Calling this function on QCborArray::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! + \fn QCborArray::ConstIterator QCborArray::ConstIterator::operator--(int) + \overload + + The postfix -- operator, \c{it--}, makes the preceding item current and + returns an iterator to the previously current item. +*/ + +/*! + \fn QCborArray::ConstIterator &QCborArray::ConstIterator::operator+=(qsizetype j) + + Advances the iterator by \a j positions. If \a j is negative, the iterator + goes backward. Returns a reference to this iterator. + + \sa operator-=(), operator+() +*/ + +/*! + \fn QCborArray::ConstIterator &QCborArray::ConstIterator::operator-=(qsizetype j) + + Makes the iterator go back by \a j positions. If \a j is negative, the + iterator goes forward. Returns a reference to this iterator. + + \sa operator+=(), operator-() +*/ + +/*! + \fn QCborArray::ConstIterator QCborArray::ConstIterator::operator+(qsizetype j) const + + Returns an iterator to the item at a position \a j steps forward from this + iterator. If \a j is negative, the iterator goes backward. + + \sa operator-(), operator+=() +*/ + +/*! + \fn QCborArray::ConstIterator QCborArray::ConstIterator::operator-(qsizetype j) const + + Returns an iterator to the item at a position \a j steps backward from this + iterator. If \a j is negative, the iterator goes forward. + + \sa operator+(), operator-=() +*/ + +/*! + \fn qsizetype QCborArray::ConstIterator::operator-(ConstIterator other) const + + Returns the offset of this iterator relative to \a other. +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qcborarray.h b/src/corelib/serialization/qcborarray.h new file mode 100644 index 0000000000..08d41bc30f --- /dev/null +++ b/src/corelib/serialization/qcborarray.h @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** 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 QCBORARRAY_H +#define QCBORARRAY_H + +#include <QtCore/qcborvalue.h> + +#include <initializer_list> + +QT_BEGIN_NAMESPACE + +class QJsonArray; + +class QCborContainerPrivate; +class Q_CORE_EXPORT QCborArray +{ +public: + class ConstIterator; + class Iterator { + mutable QCborValueRef item; + friend class ConstIterator; + friend class QCborArray; + Iterator(QCborContainerPrivate *dd, qsizetype ii) : item(dd, ii) {} + public: + typedef std::random_access_iterator_tag iterator_category; + typedef qsizetype difference_type; + typedef QCborValue value_type; + typedef QCborValueRef reference; + typedef QCborValueRef *pointer; + + Q_DECL_CONSTEXPR Iterator() = default; + Q_DECL_CONSTEXPR Iterator(const Iterator &) = default; + Iterator &operator=(const Iterator &other) + { + // rebind the reference + item.d = other.item.d; + item.i = other.item.i; + return *this; + } + + QCborValueRef operator*() const { return item; } + QCborValueRef *operator->() const { return &item; } + QCborValueRef operator[](qsizetype j) { return { item.d, item.i + j }; } + + bool operator==(const Iterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const Iterator &o) const { return !(*this == o); } + bool operator<(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + bool operator==(const ConstIterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const ConstIterator &o) const { return !(*this == o); } + bool operator<(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + Iterator &operator++() { ++item.i; return *this; } + Iterator operator++(int) { Iterator n = *this; ++item.i; return n; } + Iterator &operator--() { item.i--; return *this; } + Iterator operator--(int) { Iterator n = *this; item.i--; return n; } + Iterator &operator+=(qsizetype j) { item.i += j; return *this; } + Iterator &operator-=(qsizetype j) { item.i -= j; return *this; } + Iterator operator+(qsizetype j) const { return Iterator({ item.d, item.i + j }); } + Iterator operator-(qsizetype j) const { return Iterator({ item.d, item.i - j }); } + qsizetype operator-(Iterator j) const { return item.i - j.item.i; } + }; + + class ConstIterator { + QCborValueRef item; + friend class Iterator; + friend class QCborArray; + ConstIterator(QCborContainerPrivate *dd, qsizetype ii) : item(dd, ii) {} + public: + typedef std::random_access_iterator_tag iterator_category; + typedef qsizetype difference_type; + typedef const QCborValue value_type; + typedef const QCborValueRef reference; + typedef const QCborValueRef *pointer; + + Q_DECL_CONSTEXPR ConstIterator() = default; + Q_DECL_CONSTEXPR ConstIterator(const ConstIterator &) = default; + ConstIterator &operator=(const ConstIterator &other) + { + // rebind the reference + item.d = other.item.d; + item.i = other.item.i; + return *this; + } + + const QCborValueRef operator*() const { return item; } + const QCborValueRef *operator->() const { return &item; } + const QCborValueRef operator[](qsizetype j) { return { item.d, item.i + j }; } + + bool operator==(const Iterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const Iterator &o) const { return !(*this == o); } + bool operator<(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + bool operator==(const ConstIterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const ConstIterator &o) const { return !(*this == o); } + bool operator<(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + ConstIterator &operator++() { ++item.i; return *this; } + ConstIterator operator++(int) { ConstIterator n = *this; ++item.i; return n; } + ConstIterator &operator--() { item.i--; return *this; } + ConstIterator operator--(int) { ConstIterator n = *this; item.i--; return n; } + ConstIterator &operator+=(qsizetype j) { item.i += j; return *this; } + ConstIterator &operator-=(qsizetype j) { item.i -= j; return *this; } + ConstIterator operator+(qsizetype j) const { return ConstIterator({ item.d, item.i + j }); } + ConstIterator operator-(qsizetype j) const { return ConstIterator({ item.d, item.i - j }); } + qsizetype operator-(ConstIterator j) const { return item.i - j.item.i; } + }; + + typedef qsizetype size_type; + typedef QCborValue value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef QCborValue &reference; + typedef const QCborValue &const_reference; + typedef qsizetype difference_type; + + QCborArray() Q_DECL_NOTHROW; + QCborArray(const QCborArray &other) Q_DECL_NOTHROW; + QCborArray &operator=(const QCborArray &other) Q_DECL_NOTHROW; + QCborArray(std::initializer_list<QCborValue> args) + : QCborArray() + { + detach(qsizetype(args.size())); + for (const QCborValue &v : args) + append(v); + } + ~QCborArray(); + + void swap(QCborArray &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + } + + QCborValue toCborValue() const { return *this; } + + qsizetype size() const Q_DECL_NOTHROW; + bool isEmpty() const { return size() == 0; } + + QCborValue at(qsizetype i) const; + QCborValue first() const { return at(0); } + QCborValue last() const { return at(size() - 1); } + QCborValue operator[](qsizetype i) const { return at(i); } + QCborValueRef first() { Q_ASSERT(!isEmpty()); return begin()[0]; } + QCborValueRef last() { Q_ASSERT(!isEmpty()); return begin()[size() - 1]; } + QCborValueRef operator[](qsizetype i) { Q_ASSERT(i < size()); return begin()[i]; } + + void insert(qsizetype i, const QCborValue &value); + void prepend(const QCborValue &value) { insert(0, value); } + void append(const QCborValue &value) { insert(-1, value); } + void removeAt(qsizetype i); + QCborValue takeAt(qsizetype i) { QCborValue v = at(i); removeAt(i); return v; } + void removeFirst() { removeAt(0); } + void removeLast() { removeAt(size() - 1); } + QCborValue takeFirst() { return takeAt(0); } + QCborValue takeLast() { return takeAt(size() - 1); } + + bool contains(const QCborValue &value) const; + + int compare(const QCborArray &other) const Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION; +#if QT_HAS_INCLUDE(<compare>) + std::strong_ordering operator<=>(const QCborArray &other) const + { + int c = compare(other); + if (c > 0) return std::strong_ordering::greater; + if (c == 0) return std::strong_ordering::equivalent; + return std::strong_ordering::less; + } +#else + bool operator==(const QCborArray &other) const Q_DECL_NOTHROW + { return compare(other) == 0; } + bool operator!=(const QCborArray &other) const Q_DECL_NOTHROW + { return !(*this == other); } + bool operator<(const QCborArray &other) const + { return compare(other) < 0; } +#endif + + typedef Iterator iterator; + typedef ConstIterator const_iterator; + iterator begin() { detach(); return iterator{d.data(), 0}; } + const_iterator constBegin() const { return const_iterator{d.data(), 0}; } + const_iterator begin() const { return constBegin(); } + const_iterator cbegin() const { return constBegin(); } + iterator end() { detach(); return iterator{d.data(), size()}; } + const_iterator constEnd() const { return const_iterator{d.data(), size()}; } + const_iterator end() const { return constEnd(); } + const_iterator cend() const { return constEnd(); } + iterator insert(iterator before, const QCborValue &value) + { insert(before.item.i, value); return iterator{d.data(), before.item.i}; } + iterator erase(iterator it) { removeAt(it.item.i); return iterator{d.data(), it.item.i}; } + + void push_back(const QCborValue &t) { append(t); } + void push_front(const QCborValue &t) { prepend(t); } + void pop_front() { removeFirst(); } + void pop_back() { removeLast(); } + bool empty() const { return isEmpty(); } + + // convenience + QCborArray operator+(const QCborValue &v) const + { QCborArray n = *this; n += v; return n; } + QCborArray &operator+=(const QCborValue &v) + { append(v); return *this; } + QCborArray &operator<<(const QCborValue &v) + { append(v); return *this; } + +private: + void detach(qsizetype reserve = 0); + + friend QCborValue; + explicit QCborArray(QCborContainerPrivate &dd) Q_DECL_NOTHROW; + QExplicitlySharedDataPointer<QCborContainerPrivate> d; +}; + +Q_DECLARE_SHARED(QCborArray) + +inline QCborArray QCborValueRef::toArray() const +{ + return concrete().toArray(); +} + +inline QCborArray QCborValueRef::toArray(const QCborArray &a) const +{ + return concrete().toArray(a); +} + +QT_END_NAMESPACE + +#endif // QCBORARRAY_H diff --git a/src/corelib/serialization/qcbormap.cpp b/src/corelib/serialization/qcbormap.cpp new file mode 100644 index 0000000000..f7d58ca90b --- /dev/null +++ b/src/corelib/serialization/qcbormap.cpp @@ -0,0 +1,1553 @@ +/**************************************************************************** +** +** 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 "qcbormap.h" +#include "qcborvalue_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QCborMap + \inmodule QtCore + \ingroup cbor + \reentrant + \since 5.12 + + \brief The QCborMap class is used to hold an associative container representable in CBOR. + + This class can be used to hold an associative container in CBOR, a map + between a key and a value type. CBOR is the Concise Binary Object + Representation, a very compact form of binary data encoding that is a + superset of 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}. + + Unlike JSON and \l QVariantMap, CBOR map keys can be of any type, not just + strings. For that reason, QCborMap is effectively a map between QCborValue + keys to QCborValue value elements. + + However, for all member functions that take a key parameter, QCborMap + provides overloads that will work efficiently with integers and strings. In + fact, the use of integer keys is encouraged, since they occupy fewer bytes + to transmit and are simpler to encode and decode. Newer protocols designed + by the IETF CoRE WG to work specifically with CBOR are known to use them. + + QCborMap is not sorted, because of that, searching for keys has linear + complexity (O(n)). QCborMap actually keeps the elements in the order that + they were inserted, which means that it is possible to make sorted + QCborMaps by carefully inserting elements in sorted order. CBOR does not + require sorting, but recommends it. + + QCborMap can also be converted to and from QVariantMap and QJsonObject. + However, when performing the conversion, any non-string keys will be + stringified using a one-way method that the conversion back to QCborMap + will not undo. + + \sa QCborArray, QCborValue, QJsonDocument, QVariantMap + */ + +/*! + \typedef QCborMap::value_type + + The value that is stored in this container: a pair of QCborValues + */ + +/*! + \typedef QCborMap::key_type + + The key type for this map. Since QCborMap keys can be any CBOR type, this + is a QCborValue. + */ + +/*! + \typedef QCborMap::mapped_type + + The type that is mapped to (the value), that is, a QCborValue. + */ + +/*! + \typedef QCborMap::size_type + + The type that QCborMap uses for sizes. + */ + +/*! + \typedef QCborMap::iterator + + A synonym for QCborMap::Iterator. + */ + +/*! + \typedef QCborMap::const_iterator + + A synonym for QCborMap::ConstIterator + */ + +/*! + \fn QCborMap::iterator QCborMap::begin() + + Returns a map iterator pointing to the first key-value pair of this map. If + this map is empty, the returned iterator will be the same as end(). + + \sa constBegin(), end() + */ + +/*! + \fn QCborMap::const_iterator QCborMap::constBegin() const + + Returns a map iterator pointing to the first key-value pair of this map. If + this map is empty, the returned iterator will be the same as constEnd(). + + \sa begin(), constEnd() + */ + +/*! + \fn QCborMap::const_iterator QCborMap::begin() const + + Returns a map iterator pointing to the first key-value pair of this map. If + this map is empty, the returned iterator will be the same as constEnd(). + + \sa begin(), constEnd() + */ + +/*! + \fn QCborMap::const_iterator QCborMap::cbegin() const + + Returns a map iterator pointing to the first key-value pair of this map. If + this map is empty, the returned iterator will be the same as constEnd(). + + \sa begin(), constEnd() + */ + +/*! + \fn QCborMap::iterator QCborMap::end() + + Returns a map iterator representing an element just past the last element + in the map. + + \sa begin(), constBegin(), find(), constFind() + */ + +/*! + \fn QCborMap::iterator QCborMap::constEnd() const + + Returns a map iterator representing an element just past the last element + in the map. + + \sa begin(), constBegin(), find(), constFind() + */ + +/*! + \fn QCborMap::iterator QCborMap::end() const + + Returns a map iterator representing an element just past the last element + in the map. + + \sa begin(), constBegin(), find(), constFind() + */ + +/*! + \fn QCborMap::iterator QCborMap::cend() const + + Returns a map iterator representing an element just past the last element + in the map. + + \sa begin(), constBegin(), find(), constFind() + */ + +/*! + Constructs an empty CBOR Map object. + + \sa isEmpty() + */ +QCborMap::QCborMap() Q_DECL_NOTHROW + : d(nullptr) +{ +} + +/*! + Creates a QCborMap object that is a copy of \a other. + */ +QCborMap::QCborMap(const QCborMap &other) Q_DECL_NOTHROW + : d(other.d) +{ +} + +/*! + \fn QCborMap::QCborMap(std::initializer_list<value_type> args) + + Constructs a QCborMap with items from a brace-initialization list found in + \a args, as in the following example: + + \code + QCborMap map = { + {0, "Hello"}, + {1, "World"}, + {"foo", nullptr}, + {"bar", QCborArray{0, 1, 2, 3, 4}} + }; + \endcode + */ + +/*! + Destroys this QCborMap object and frees any associated resources it owns. + */ +QCborMap::~QCborMap() +{ +} + +/*! + Replaces the contents of this object with a copy of \a other, then returns + a reference to this object. + */ +QCborMap &QCborMap::operator=(const QCborMap &other) Q_DECL_NOTHROW +{ + d = other.d; + return *this; +} + +/*! + \fn void QCborMap::swap(QCborMap &other) + + Swaps the contents of this map and \a other. + */ + +/*! + \fn QCborValue QCborMap::toCborValue() const + + Explicitly constructs a \l QCborValue object that represents this map. + This function is usually not necessary since QCborValue has a constructor + for QCborMap, so the conversion is implicit. + + Converting QCborMap to QCborValue allows it to be used in any context where + QCborValues can be used, including as keys and mapped types in QCborMap, as + well as QCborValue::toCbor(). + + \sa QCborValue::QCborValue(const QCborMap &) + */ + +/*! + \fn bool QCborMap::isEmpty() const + + Returns true if this map is empty (that is, size() is 0). + + \sa size() + */ + +/*! + Returns the number of elements in this map. + + \sa isEmpty() + */ +qsizetype QCborMap::size() const Q_DECL_NOTHROW +{ + return d ? d->elements.size() / 2 : 0; +} + +/*! + Returns a list of all keys in this map. + + \sa QMap::keys(), QHash::keys() + */ +QVector<QCborValue> QCborMap::keys() const +{ + QVector<QCborValue> result; + if (d) { + result.reserve(size()); + for (qsizetype i = 0; i < d->elements.size(); i += 2) + result << d->valueAt(i); + } + return result; +} + +/*! + \fn QCborValue QCborMap::value(qint64 key) const + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. CBOR recommends using integer keys, since they occupy less + space and are simpler to encode and decode. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one the return from function will reference. QCborMap does not allow + inserting duplicate keys, but it is possible to create such a map by + decoding a CBOR stream with them. They are usually not permitted and having + duplicate keys is usually an indication of a problem in the sender. + + \sa operator[](qint64), find(qint64), constFind(qint64), remove(qint64), contains(qint64) + value(QLatin1String), value(const QString &), value(const QCborValue &) + */ + +/*! + \fn QCborValue QCborMap::operator[](qint64 key) const + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. CBOR recommends using integer keys, since they occupy less + space and are simpler to encode and decode. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(qint64), find(qint64), constFind(qint64), remove(qint64), contains(qint64) + operator[](QLatin1String), operator[](const QString &), operator[](const QCborOperator[] &) + */ + +/*! + \fn void QCborMap::remove(qint64 key) + + Removes the key \a key and the corresponding value from the map, if it is + found. If the map contains no such key, this function does nothing. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will remove. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(qint64), operator[](qint64), find(qint64), contains(qint64) + remove(QLatin1String), remove(const QString &), remove(const QCborValue &) + */ + +/*! + \fn bool QCborMap::contains(qint64 key) + + Returns true if this map contains a key-value pair identified by key \a + key. CBOR recommends using integer keys, since they occupy less space and + are simpler to encode and decode. + + \sa value(qint64), operator[](qint64), find(qint64), remove(qint64), + contains(QLatin1String), remove(const QString &), remove(const QCborValue &) + */ + +/*! + Returns a QCborValueRef to the value in this map that corresponds to key \a + key. CBOR recommends using integer keys, since they occupy less space and + are simpler to encode and decode. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + If the map did not have a key equal to \a key, one is inserted and this + function returns a reference to the new value, which will be a QCborValue + with an undefined value. For that reason, it is not possible with this + function to tell apart the situation where the key was not present from the + situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one the return will reference. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(qint64), find(qint64), contains(qint64), remove(qint64), + operator[](QLatin1String), operator[](const QString &), operator[](const QCborValue &) + */ +QCborValueRef QCborMap::operator[](qint64 key) +{ + auto it = find(key); + if (it == constEnd()) { + // insert element + detach(it.item.i + 2); + d->append(key); + d->append(Undefined{}); + } + return { d.data(), it.item.i }; +} + +/*! + \fn QCborValue QCborMap::value(QLatin1String key) const + \overload + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa operator[](QLatin1String), find(QLatin1String), constFind(QLatin1String), + remove(QLatin1String), contains(QLatin1String) + value(qint64), value(const QString &), value(const QCborValue &) + */ + +/*! + \fn QCborValue QCborMap::operator[](QLatin1String key) const + \overload + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(QLatin1String), find(QLatin1String), constFind(QLatin1String), + remove(QLatin1String), contains(QLatin1String) + operator[](qint64), operator[](const QString &), operator[](const QCborOperator[] &) + */ + +/*! + \fn void QCborMap::remove(QLatin1String key) + \overload + + Removes the key \a key and the corresponding value from the map, if it is + found. If the map contains no such key, this function does nothing. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will remove. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(QLatin1String), operator[](QLatin1String), find(QLatin1String), contains(QLatin1String) + remove(qint64), remove(const QString &), remove(const QCborValue &) + */ + +/*! + \fn bool QCborMap::contains(QLatin1String key) + \overload + + Returns true if this map contains a key-value pair identified by key \a + key. + + \sa value(QLatin1String), operator[](QLatin1String), find(QLatin1String), remove(QLatin1String), + contains(qint64), remove(const QString &), remove(const QCborValue &) + */ + +/*! + \overload + + Returns a QCborValueRef to the value in this map that corresponds to key \a + key. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + If the map did not have a key equal to \a key, one is inserted and this + function returns a reference to the new value, which will be a QCborValue + with an undefined value. For that reason, it is not possible with this + function to tell apart the situation where the key was not present from the + situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one the return will reference. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(QLatin1String), find(QLatin1String), contains(QLatin1String), remove(QLatin1String), + operator[](qint64), operator[](const QString &), operator[](const QCborValue &) + */ +QCborValueRef QCborMap::operator[](QLatin1String key) +{ + auto it = find(key); + if (it == constEnd()) { + // insert element + detach(it.item.i + 2); + d->append(key); + d->append(Undefined{}); + } + return { d.data(), it.item.i }; +} + +/*! + \fn QCborValue QCborMap::value(const QString &key) const + \overload + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa operator[](const QString &), find(const QString &), constFind(const QString &), + remove(const QString &), contains(const QString &) + value(qint64), value(QLatin1String), value(const QCborValue &) + */ + +/*! + \fn QCborValue QCborMap::operator[](const QString &key) const + \overload + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QString &), find(const QString &), constFind(const QString &), + remove(const QString &), contains(const QString &) + operator[](qint64), operator[](QLatin1String), operator[](const QCborOperator[] &) + */ + +/*! + \fn void QCborMap::remove(const QString &key) + \overload + + Removes the key \a key and the corresponding value from the map, if it is + found. If the map contains no such key, this function does nothing. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will remove. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QString &), operator[](const QString &), find(const QString &), + contains(const QString &) + remove(qint64), remove(QLatin1String), remove(const QCborValue &) + */ + +/*! + \fn bool QCborMap::contains(const QString &key) + \overload + + Returns true if this map contains a key-value pair identified by key \a + key. + + \sa value(const QString &), operator[](const QString &), find(const QString &), + remove(const QString &), + contains(qint64), remove(QLatin1String), remove(const QCborValue &) + */ + +/*! + \overload + + Returns a QCborValueRef to the value in this map that corresponds to key \a + key. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + If the map did not have a key equal to \a key, one is inserted and this + function returns a reference to the new value, which will be a QCborValue + with an undefined value. For that reason, it is not possible with this + function to tell apart the situation where the key was not present from the + situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one the return will reference. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QString &), find(const QString &), contains(const QString &), remove(const QString &), + operator[](qint64), operator[](QLatin1String), operator[](const QCborValue &) + */ +QCborValueRef QCborMap::operator[](const QString & key) +{ + auto it = find(key); + if (it == constEnd()) { + // insert element + detach(it.item.i + 2); + d->append(key); + d->append(Undefined{}); + } + return { d.data(), it.item.i }; +} + +/*! + \fn QCborValue QCborMap::value(const QCborValue &key) const + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa operator[](const QCborValue &), find(const QCborValue &), constFind(const QCborValue &), + remove(const QCborValue &), contains(const QCborValue &) + value(qint64), value(QLatin1String), value(const QString &) + */ + +/*! + \fn QCborValue QCborMap::operator[](const QCborValue &key) const + + Returns the QCborValue element in this map that corresponds to key \a key, + if there is one. + + If the map does not contain key \a key, this function returns a QCborValue + containing an undefined value. For that reason, it is not possible with + this function to tell apart the situation where the key was not present + from the situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will return. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QCborValue &), find(const QCborValue &), constFind(const QCborValue &), + remove(const QCborValue &), contains(const QCborValue &) + operator[](qint64), operator[](QLatin1String), operator[](const QCborOperator[] &) + */ + +/*! + \fn void QCborMap::remove(const QCborValue &key) + + Removes the key \a key and the corresponding value from the map, if it is + found. If the map contains no such key, this function does nothing. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will remove. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QCborValue &), operator[](const QCborValue &), find(const QCborValue &), + contains(const QCborValue &) + remove(qint64), remove(QLatin1String), remove(const QString &) + */ + +/*! + \fn bool QCborMap::contains(const QCborValue &key) + + Returns true if this map contains a key-value pair identified by key \a + key. + + \sa value(const QCborValue &), operator[](const QCborValue &), find(const QCborValue &), + remove(const QCborValue &), + contains(qint64), remove(QLatin1String), remove(const QString &) + */ + +/*! + \overload + + Returns a QCborValueRef to the value in this map that corresponds to key \a + key. + + QCborValueRef has the exact same API as \l QCborValue, with one important + difference: if you assign new values to it, this map will be updated with + that new value. + + If the map did not have a key equal to \a key, one is inserted and this + function returns a reference to the new value, which will be a QCborValue + with an undefined value. For that reason, it is not possible with this + function to tell apart the situation where the key was not present from the + situation where the key was mapped to an undefined value. + + If the map contains more than one key equal to \a key, it is undefined + which one the return will reference. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QCborValue &), find(const QCborValue &), contains(const QCborValue &), remove(const QCborValue &), + operator[](qint64), operator[](QLatin1String), operator[](const QString &) + */ +QCborValueRef QCborMap::operator[](const QCborValue &key) +{ + auto it = find(key); + if (it == constEnd()) { + // insert element + detach(it.item.i + 2); + d->append(key); + d->append(Undefined{}); + } + return { d.data(), it.item.i }; +} + +/*! + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns end(). + + CBOR recommends using integer keys, since they occupy less + space and are simpler to encode and decode. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(qint64), operator[](qint64), constFind(qint64), remove(qint64), contains(qint64) + value(QLatin1String), value(const QString &), value(const QCborValue &) + */ +QCborMap::iterator QCborMap::find(qint64 key) +{ + auto it = constFind(key); + if (it != constEnd()) + detach(); + return { d.data(), it.item.i }; +} + +/*! + \overload + + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns end(). + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(QLatin1String), operator[](QLatin1String), constFind(QLatin1String), + remove(QLatin1String), contains(QLatin1String) + value(qint64), value(const QString &), value(const QCborValue &) + */ +QCborMap::iterator QCborMap::find(QLatin1String key) +{ + auto it = constFind(key); + if (it != constEnd()) + detach(); + return { d.data(), it.item.i }; +} + +/*! + \overload + + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns end(). + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QString &), operator[](const QString &), constFind(const QString &), + remove(const QString &), contains(const QString &) + value(qint64), value(QLatin1String), value(const QCborValue &) + */ +QCborMap::iterator QCborMap::find(const QString & key) +{ + auto it = constFind(key); + if (it != constEnd()) + detach(); + return { d.data(), it.item.i }; +} + +/*! + \overload + + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns end(). + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QCborValue &), operator[](const QCborValue &), constFind(const QCborValue &), + remove(const QCborValue &), contains(const QCborValue &) + value(qint64), value(QLatin1String), value(const QString &) + */ +QCborMap::iterator QCborMap::find(const QCborValue &key) +{ + auto it = constFind(key); + if (it != constEnd()) + detach(); + return { d.data(), it.item.i }; +} + +/*! + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns constEnd(). + + CBOR recommends using integer keys, since they occupy less + space and are simpler to encode and decode. + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(qint64), operator[](qint64), find(qint64), remove(qint64), contains(qint64) + value(QLatin1String), value(const QString &), value(const QCborValue &) + */ +QCborMap::const_iterator QCborMap::constFind(qint64 key) const +{ + for (qsizetype i = 0; i < 2 * size(); i += 2) { + const auto &e = d->elements.at(i); + if (e.type == QCborValue::Integer && e.value == key) + return { d.data(), i + 1 }; + } + return constEnd(); +} + +/*! + \overload + + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns constEnd(). + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(QLatin1String), operator[](QLatin1String), find(QLatin1String), + remove(QLatin1String), contains(QLatin1String) + value(qint64), value(const QString &), value(const QCborValue &) + */ +QCborMap::const_iterator QCborMap::constFind(QLatin1String key) const +{ + for (qsizetype i = 0; i < 2 * size(); i += 2) { + if (d->stringEqualsElement(i, key)) + return { d.data(), i + 1 }; + } + return constEnd(); +} + +/*! + \overload + + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns constEnd(). + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QString &), operator[](const QString &), find(const QString &), + remove(const QString &), contains(const QString &) + value(qint64), value(QLatin1String), value(const QCborValue &) + */ +QCborMap::const_iterator QCborMap::constFind(const QString & key) const +{ + for (qsizetype i = 0; i < 2 * size(); i += 2) { + if (d->stringEqualsElement(i, key)) + return { d.data(), i + 1 }; + } + return constEnd(); +} + +/*! + \overload + + Returns a map iterator to the key-value pair whose key is \a key, if the + map contains such a pair. If it doesn't, this function returns constEnd(). + + If the map contains more than one key equal to \a key, it is undefined + which one this function will find. QCborMap does not allow inserting + duplicate keys, but it is possible to create such a map by decoding a CBOR + stream with them. They are usually not permitted and having duplicate keys + is usually an indication of a problem in the sender. + + \sa value(const QCborValue &), operator[](const QCborValue &), find(const QCborValue &), + remove(const QCborValue &), contains(const QCborValue &), + value(qint64), value(QLatin1String), value(const QString &) + */ +QCborMap::const_iterator QCborMap::constFind(const QCborValue &key) const +{ + for (qsizetype i = 0; i < 2 * size(); i += 2) { + int cmp = d->compareElement(i, key); + if (cmp == 0) + return { d.data(), i + 1 }; + } + return constEnd(); +} + +/*! + \fn QCborMap::iterator QCborMap::insert(qint64 key, const QCborValue &value) + \overload + + Inserts the key \a key and value \a value into this map and returns a map + iterator pointing to the newly inserted pair. + + If the map already had a key equal to \a key, its value will be overwritten + by \a value. + + \sa erase(), remove(qint64), value(qint64), operator[](qint64), find(qint64), + contains(qint64) + */ + +/*! + \fn QCborMap::iterator QCborMap::insert(QLatin1String key, const QCborValue &value) + \overload + + Inserts the key \a key and value \a value into this map and returns a map + iterator pointing to the newly inserted pair. + + If the map already had a key equal to \a key, its value will be overwritten + by \a value. + + \sa erase(), remove(QLatin1String), value(QLatin1String), operator[](QLatin1String), + find(QLatin1String), contains(QLatin1String) + */ + +/*! + \fn QCborMap::iterator QCborMap::insert(const QString &key, const QCborValue &value) + \overload + + Inserts the key \a key and value \a value into this map and returns a map + iterator pointing to the newly inserted pair. + + If the map already had a key equal to \a key, its value will be overwritten + by \a value. + + \sa erase(), remove(const QString &), value(const QString &), operator[](const QString &), + find(const QString &), contains(const QString &) + */ + +/*! + \fn QCborMap::iterator QCborMap::insert(const QCborValue &key, const QCborValue &value) + \overload + + Inserts the key \a key and value \a value into this map and returns a map + iterator pointing to the newly inserted pair. + + If the map already had a key equal to \a key, its value will be overwritten + by \a value. + + \sa erase(), remove(const QCborValue &), value(const QCborValue &), operator[](const QCborValue &), + find(const QCborValue &), contains(const QCborValue &) + */ + +/*! + \fn QCborMap::iterator QCborMap::insert(value_type v) + \overload + + Inserts the key-value pair in \a v into this map and returns a map iterator + pointing to the newly inserted pair. + + If the map already had a key equal to \c{v.first}, its value will be + overwritten by \c{v.second}. + + \sa operator[], erase() + */ + + +/*! + \fn QCborMap::iterator QCborMap::erase(const_iterator it) + + Removes the key-value pair pointed to by the map iterator \a it and returns a + pointer to the next element, after removal. + + \sa remove(), begin(), end(), insert() + */ + +/*! + \overload + + Removes the key-value pair pointed to by the map iterator \a it and returns a + pointer to the next element, after removal. + + \sa remove(), begin(), end(), insert() + */ +QCborMap::iterator QCborMap::erase(QCborMap::iterator it) +{ + detach(); + + // remove both key and value + // ### optimize? + d->removeAt(it.item.i - 1); + d->removeAt(it.item.i - 1); + return it; +} + +/*! + \fn bool QCborMap::empty() const + + Synonym for isEmpty(). This function is provided for compatibility with + generic code that uses the Standard Library API. + + Returns true if this map is empty (size() == 0). + + \sa isEmpty(), size() + */ + +/*! + \fn int QCborMap::compare(const QCborMap &other) const + + Compares this map and \a other, comparing each element in sequence, and + returns an integer that indicates whether this map should be sorted prior + to (if the result is negative) or after \a other (if the result is + positive). If this function returns 0, the two maps are equal and contain + the same elements. + + Note that CBOR maps are unordered, which means that two maps containing the + very same pairs but in different order will still compare differently. To + avoid this, it is recommended to insert elements into the map in a + predictable order, such as by ascending key value. In fact, maps with keys + in sorted order are required for Canonical CBOR representation. + + For more information on CBOR sorting order, see QCborValue::compare(). + + \sa QCborValue::compare(), QCborArray::compare(), operator==() + */ + +/*! + \fn bool QCborMap::operator==(const QCborMap &other) const + + Compares this map and \a other, comparing each element in sequence, and + returns true if the two maps contains the same elements in the same order, + false otherwise. + + Note that CBOR maps are unordered, which means that two maps containing the + very same pairs but in different order will still compare differently. To + avoid this, it is recommended to insert elements into the map in a + predictable order, such as by ascending key value. In fact, maps with keys + in sorted order are required for Canonical CBOR representation. + + For more information on CBOR equality in Qt, see, QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator!=(), operator<() + */ + +/*! + \fn bool QCborMap::operator!=(const QCborMap &other) const + + Compares this map and \a other, comparing each element in sequence, and + returns true if the two maps contains any different elements or elements in + different orders, false otherwise. + + Note that CBOR maps are unordered, which means that two maps containing the + very same pairs but in different order will still compare differently. To + avoid this, it is recommended to insert elements into the map in a + predictable order, such as by ascending key value. In fact, maps with keys + in sorted order are required for Canonical CBOR representation. + + For more information on CBOR equality in Qt, see, QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator==(), operator<() + */ + +/*! + \fn bool QCborMap::operator<(const QCborMap &other) const + + Compares this map and \a other, comparing each element in sequence, and + returns true if this map should be sorted before \a other, false + otherwise. + + Note that CBOR maps are unordered, which means that two maps containing the + very same pairs but in different order will still compare differently. To + avoid this, it is recommended to insert elements into the map in a + predictable order, such as by ascending key value. In fact, maps with keys + in sorted order are required for Canonical CBOR representation. + + For more information on CBOR sorting order, see QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator==(), operator!=() + */ + +void QCborMap::detach(qsizetype reserved) +{ + d = QCborContainerPrivate::detach(d.data(), reserved ? reserved : size() * 2); +} + +/*! + \class QCborMap::Iterator + \inmodule QtCore + \ingroup cbor + \reentrant + \since 5.12 + + \brief The QCborMap::Iterator class provides an STL-style non-const iterator for QCborMap. + + QCborMap::Iterator allows you to iterate over a QCborMap and to modify the + value (but not the key) stored under a particular key. If you want to + iterate over a const QCborMap, you should use QCborMap::ConstIterator. It + is generally good practice to use QCborMap::ConstIterator on a non-const + QCborMap as well, unless you need to change the QCborMap through the + iterator. Const iterators are slightly faster, and improve code + readability. + + You must initialize the iterator using a QCborMap function like + QCborMap::begin(), QCborMap::end(), or QCborMap::find() before you can + start iterating.. + + Multiple iterators can be used on the same object. Existing iterators will however + become dangling once the object gets modified. + + \sa QCborMap::ConstIterator +*/ + +/*! + \typedef QCborMap::Iterator::difference_type + \internal +*/ + +/*! + \typedef QCborMap::Iterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random-access iterator. +*/ + +/*! + \typedef QCborMap::Iterator::reference + \internal +*/ + +/*! + \typedef QCborMap::Iterator::value_type + \internal +*/ + +/*! + \typedef QCborMap::Iterator::pointer + \internal +*/ + +/*! + \fn QCborMap::Iterator::Iterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa QCborMap::begin(), QCborMap::end() +*/ + +/*! + \fn QCborMap::Iterator::Iterator(const Iterator &other) + + Constructs an iterator as a copy of \a other. + */ + +/*! + \fn QCborMap::Iterator &QCborMap::Iterator::operator=(const Iterator &other) + + Makes this iterator a copy of \a other and returns a reference to this + iterator. + */ + +/*! + \fn QCborValue QCborMap::Iterator::key() const + + Returns the current item's key. + + There is no direct way of changing an item's key through an iterator, + although it can be done by calling QCborMap::erase() followed by + QCborMap::insert(). + + \sa value() +*/ + +/*! + \fn QCborValueRef QCborMap::Iterator::value() const + + Returns a modifiable reference to the current item's value. + + You can change the value for a key by using value() on the left side of an + assignment. + + The return value is of type QCborValueRef, a helper class for QCborArray + and QCborMap. When you get an object of type QCborValueRef, you can use it + as if it were a reference to a QCborValue. If you assign to it, the + assignment will apply to the element in the QCborArray or QCborMap from + which you got the reference. + + \sa key(), operator*() +*/ + +/*! + \fn QCborMap::Iterator::value_type QCborMap::Iterator::operator*() const + + Returns a pair containing the current item's key and a modifiable reference + to the current item's value. + + The second element of the pair is of type QCborValueRef, a helper class for + QCborArray and QCborMap. When you get an object of type QCborValueRef, you + can use it as if it were a reference to a QCborValue. If you assign to it, + the assignment will apply to the element in the QCborArray or QCborMap from + which you got the reference. + + \sa key(), value() +*/ + +/*! + \fn QCborValueRef *QCborMap::Iterator::operator->() const + + Returns a pointer to a modifiable reference to the current pair's value. +*/ + +/*! + \fn bool QCborMap::Iterator::operator==(const Iterator &other) const + \fn bool QCborMap::Iterator::operator==(const ConstIterator &other) const + + Returns \c true if \a other points to the same entry in the map as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QCborMap::Iterator::operator!=(const Iterator &other) const + \fn bool QCborMap::Iterator::operator!=(const ConstIterator &other) const + + Returns \c true if \a other points to a different entry in the map than + this iterator; otherwise returns \c false. + + \sa operator==() +*/ + +/*! + \fn QCborMap::Iterator &QCborMap::Iterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the next item in + the map and returns this iterator. + + Calling this function on QCborMap::end() leads to undefined results. + + \sa operator--() +*/ + +/*! + \fn QCborMap::Iterator QCborMap::Iterator::operator++(int) + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the next item in + the map and returns an iterator to the previously current item. +*/ + +/*! + \fn QCborMap::Iterator QCborMap::Iterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item current and + returns this iterator. + + Calling this function on QCborMap::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! + \fn QCborMap::Iterator QCborMap::Iterator::operator--(int) + \overload + + The postfix -- operator, \c{i--}, makes the preceding item current and + returns an iterator pointing to the previously current item. +*/ + +/*! + \fn QCborMap::Iterator QCborMap::Iterator::operator+(qsizetype j) const + + Returns an iterator to the item at \a j positions forward from this + iterator. If \a j is negative, the iterator goes backward. + + \sa operator-() +*/ + +/*! + \fn QCborMap::Iterator QCborMap::Iterator::operator-(qsizetype j) const + + Returns an iterator to the item at \a j positions backward from this + iterator. If \a j is negative, the iterator goes forward. + + \sa operator+() +*/ + +/*! + \fn QCborMap::Iterator &QCborMap::Iterator::operator+=(qsizetype j) + + Advances the iterator by \a j items. If \a j is negative, the iterator goes + backward. Returns a reference to this iterator. + + \sa operator-=(), operator+() +*/ + +/*! + \fn QCborMap::Iterator &QCborMap::Iterator::operator-=(qsizetype j) + + Makes the iterator go back by \a j items. If \a j is negative, the iterator + goes forward. Returns a reference to this iterator. + + \sa operator+=(), operator-() +*/ + +/*! + \class QCborMap::ConstIterator + \inmodule QtCore + \ingroup cbor + \since 5.12 + + \brief The QCborMap::ConstIterator class provides an STL-style const iterator for QCborMap. + + QCborMap::ConstIterator allows you to iterate over a QCborMap. If you want + to modify the QCborMap as you iterate over it, you must use + QCborMap::Iterator instead. It is generally good practice to use + QCborMap::ConstIterator, even on a non-const QCborMap, when you don't need + to change the QCborMap through the iterator. Const iterators are slightly + faster and improve code readability. + + You must initialize the iterator using a QCborMap function like + QCborMap::begin(), QCborMap::end(), or QCborMap::find() before you can + start iterating.. + + Multiple iterators can be used on the same object. Existing iterators + will however become dangling if the object gets modified. + + \sa QCborMap::Iterator +*/ + +/*! + \typedef QCborMap::ConstIterator::difference_type + \internal +*/ + +/*! + \typedef QCborMap::ConstIterator::iterator_category + + A synonym for \e {std::random_access_iterator_tag} indicating + this iterator is a random-access iterator. +*/ + +/*! + \typedef QCborMap::ConstIterator::reference + \internal +*/ + +/*! + \typedef QCborMap::ConstIterator::value_type + \internal +*/ + +/*! + \typedef QCborMap::ConstIterator::pointer + \internal +*/ + +/*! + \fn QCborMap::ConstIterator::ConstIterator() + + Constructs an uninitialized iterator. + + Functions like key(), value(), and operator++() must not be + called on an uninitialized iterator. Use operator=() to assign a + value to it before using it. + + \sa QCborMap::constBegin(), QCborMap::constEnd() +*/ + +/*! + \fn QCborMap::ConstIterator::ConstIterator(const ConstIterator &other) + + Constructs an iterator as a copy of \a other. + */ + +/*! + \fn QCborMap::ConstIterator &QCborMap::ConstIterator::operator=(const ConstIterator &other) + + Makes this iterator a copy of \a other and returns a reference to this + iterator. + */ + +/*! + \fn QString QCborMap::ConstIterator::key() const + + Returns the current item's key. + + \sa value() +*/ + +/*! + \fn QCborValue QCborMap::ConstIterator::value() const + + Returns the current item's value. + + \sa key(), operator*() +*/ + +/*! + \fn QCborMap::ConstIterator::value_type QCborMap::ConstIterator::operator*() const + + Returns a pair containing the curent item's key and value. + + \sa key(), value() + */ + +/*! + \fn const QCborValueRef *QCborMap::ConstIterator::operator->() const + + Returns a pointer to the current pair's value. + */ + +/*! + \fn bool QCborMap::ConstIterator::operator==(const ConstIterator &other) const + \fn bool QCborMap::ConstIterator::operator==(const Iterator &other) const + + Returns \c true if \a other points to the same entry in the map as this + iterator; otherwise returns \c false. + + \sa operator!=() +*/ + +/*! + \fn bool QCborMap::ConstIterator::operator!=(const ConstIterator &other) const + \fn bool QCborMap::ConstIterator::operator!=(const Iterator &other) const + + Returns \c true if \a other points to a different entry in the map than + this iterator; otherwise returns \c false. + + \sa operator==() + */ + +/*! + \fn QCborMap::ConstIterator &QCborMap::ConstIterator::operator++() + + The prefix ++ operator, \c{++i}, advances the iterator to the next item in + the map and returns this iterator. + + Calling this function on QCborMap::end() leads to undefined results. + + \sa operator--() +*/ + +/*! + \fn QCborMap::ConstIterator QCborMap::ConstIterator::operator++(int) + \overload + + The postfix ++ operator, \c{i++}, advances the iterator to the next item in + the map and returns an iterator to the previously current item. + */ + +/*! + \fn QCborMap::ConstIterator &QCborMap::ConstIterator::operator--() + + The prefix -- operator, \c{--i}, makes the preceding item current and + returns this iterator. + + Calling this function on QCborMap::begin() leads to undefined results. + + \sa operator++() +*/ + +/*! + \fn QCborMap::ConstIterator QCborMap::ConstIterator::operator--(int) + \overload + + The postfix -- operator, \c{i--}, makes the preceding item current and + returns an iterator pointing to the previously current item. + */ + +/*! + \fn QCborMap::ConstIterator QCborMap::ConstIterator::operator+(qsizetype j) const + + Returns an iterator to the item at \a j positions forward from this + iterator. If \a j is negative, the iterator goes backward. + + \sa operator-() +*/ + +/*! + \fn QCborMap::ConstIterator QCborMap::ConstIterator::operator-(qsizetype j) const + + Returns an iterator to the item at \a j positions backward from this + iterator. If \a j is negative, the iterator goes forward. + + \sa operator+() +*/ + +/*! + \fn QCborMap::ConstIterator &QCborMap::ConstIterator::operator+=(qsizetype j) + + Advances the iterator by \a j items. If \a j is negative, the iterator goes + backward. Returns a reference to this iterator. + + \sa operator-=(), operator+() +*/ + +/*! + \fn QCborMap::ConstIterator &QCborMap::ConstIterator::operator-=(qsizetype j) + + Makes the iterator go back by \a j items. If \a j is negative, the iterator + goes forward. Returns a reference to this iterator. + + \sa operator+=(), operator-() +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qcbormap.h b/src/corelib/serialization/qcbormap.h new file mode 100644 index 0000000000..f8adba4c56 --- /dev/null +++ b/src/corelib/serialization/qcbormap.h @@ -0,0 +1,320 @@ +/**************************************************************************** +** +** 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 QCBORMAP_H +#define QCBORMAP_H + +#include <QtCore/qcborvalue.h> +#include <QtCore/qpair.h> + +#include <initializer_list> + +QT_BEGIN_NAMESPACE + +template <class Key, class T> class QMap; +typedef QMap<QString, QVariant> QVariantMap; +template <class Key, class T> class QHash; +typedef QHash<QString, QVariant> QVariantHash; +class QJsonObject; + +class QCborContainerPrivate; +class Q_CORE_EXPORT QCborMap +{ +public: + typedef QPair<QCborValue, QCborValue> value_type; + typedef QCborValue key_type; + typedef QCborValue mapped_type; + typedef qsizetype size_type; + + class ConstIterator; + class Iterator { + mutable QCborValueRef item; // points to the value + friend class ConstIterator; + friend class QCborMap; + Iterator(QCborContainerPrivate *dd, qsizetype ii) : item(dd, ii) {} + public: + typedef std::random_access_iterator_tag iterator_category; + typedef qsizetype difference_type; + typedef QPair<const QCborValueRef, QCborValueRef> value_type; + typedef QPair<const QCborValueRef, QCborValueRef> reference; + typedef QPair<const QCborValueRef, QCborValueRef> pointer; + + Q_DECL_CONSTEXPR Iterator() = default; + Q_DECL_CONSTEXPR Iterator(const Iterator &) = default; + Iterator &operator=(const Iterator &other) + { + // rebind the reference + item.d = other.item.d; + item.i = other.item.i; + return *this; + } + + value_type operator*() const { return { {item.d, item.i - 1}, item }; } + QCborValueRef *operator->() const { return &item; } + QCborValue key() const { return QCborValueRef(item.d, item.i - 1); } + QCborValueRef value() const { return item; } + + bool operator==(const Iterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const Iterator &o) const { return !(*this == o); } + bool operator<(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + bool operator==(const ConstIterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const ConstIterator &o) const { return !(*this == o); } + bool operator<(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + Iterator &operator++() { item.i += 2; return *this; } + Iterator operator++(int) { Iterator n = *this; item.i += 2; return n; } + Iterator &operator--() { item.i -= 2; return *this; } + Iterator operator--(int) { Iterator n = *this; item.i -= 2; return n; } + Iterator &operator+=(qsizetype j) { item.i += 2 * j; return *this; } + Iterator &operator-=(qsizetype j) { item.i -= 2 * j; return *this; } + Iterator operator+(qsizetype j) const { return Iterator({ item.d, item.i + 2 * j }); } + Iterator operator-(qsizetype j) const { return Iterator({ item.d, item.i - 2 * j }); } + qsizetype operator-(Iterator j) const { return (item.i - j.item.i) / 2; } + }; + + class ConstIterator { + QCborValueRef item; // points to the value + friend class Iterator; + friend class QCborMap; + ConstIterator(QCborContainerPrivate *dd, qsizetype ii) : item(dd, ii) {} + public: + typedef std::random_access_iterator_tag iterator_category; + typedef qsizetype difference_type; + typedef QPair<const QCborValueRef, const QCborValueRef> value_type; + typedef QPair<const QCborValueRef, const QCborValueRef> reference; + typedef QPair<const QCborValueRef, const QCborValueRef> pointer; + + Q_DECL_CONSTEXPR ConstIterator() = default; + Q_DECL_CONSTEXPR ConstIterator(const ConstIterator &) = default; + ConstIterator &operator=(const ConstIterator &other) + { + // rebind the reference + item.d = other.item.d; + item.i = other.item.i; + return *this; + } + + value_type operator*() const { return { {item.d, item.i - 1}, item }; } + const QCborValueRef *operator->() const { return &item; } + QCborValue key() const { return QCborValueRef(item.d, item.i - 1); } + QCborValueRef value() const { return item; } + + bool operator==(const Iterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const Iterator &o) const { return !(*this == o); } + bool operator<(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const Iterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + bool operator==(const ConstIterator &o) const { return item.d == o.item.d && item.i == o.item.i; } + bool operator!=(const ConstIterator &o) const { return !(*this == o); } + bool operator<(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i < other.item.i; } + bool operator<=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i <= other.item.i; } + bool operator>(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i > other.item.i; } + bool operator>=(const ConstIterator& other) const { Q_ASSERT(item.d == other.item.d); return item.i >= other.item.i; } + ConstIterator &operator++() { item.i += 2; return *this; } + ConstIterator operator++(int) { ConstIterator n = *this; item.i += 2; return n; } + ConstIterator &operator--() { item.i -= 2; return *this; } + ConstIterator operator--(int) { ConstIterator n = *this; item.i -= 2; return n; } + ConstIterator &operator+=(qsizetype j) { item.i += 2 * j; return *this; } + ConstIterator &operator-=(qsizetype j) { item.i -= 2 * j; return *this; } + ConstIterator operator+(qsizetype j) const { return ConstIterator({ item.d, item.i + 2 * j }); } + ConstIterator operator-(qsizetype j) const { return ConstIterator({ item.d, item.i - 2 * j }); } + qsizetype operator-(ConstIterator j) const { return (item.i - j.item.i) / 2; } + }; + + QCborMap() Q_DECL_NOTHROW; + QCborMap(const QCborMap &other) Q_DECL_NOTHROW; + QCborMap &operator=(const QCborMap &other) Q_DECL_NOTHROW; + QCborMap(std::initializer_list<value_type> args) + : QCborMap() + { + detach(args.size()); + for (auto pair : args) + insert(pair.first, pair.second); + } + ~QCborMap(); + + void swap(QCborMap &other) Q_DECL_NOTHROW + { + qSwap(d, other.d); + } + + QCborValue toCborValue() const { return *this; } + + qsizetype size() const Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION; + bool isEmpty() const { return size() == 0; } + QVector<QCborValue> keys() const; + + QCborValue value(qint64 key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue value(QLatin1String key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue value(const QString & key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue value(const QCborValue &key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue operator[](qint64 key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue operator[](QLatin1String key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue operator[](const QString & key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValue operator[](const QCborValue &key) const + { const_iterator it = find(key); return it == end() ? QCborValue() : it.value(); } + QCborValueRef operator[](qint64 key); + QCborValueRef operator[](QLatin1String key); + QCborValueRef operator[](const QString & key); + QCborValueRef operator[](const QCborValue &key); + + void remove(qint64 key) + { iterator it = find(key); if (it != end()) erase(it); } + void remove(QLatin1String key) + { iterator it = find(key); if (it != end()) erase(it); } + void remove(const QString & key) + { iterator it = find(key); if (it != end()) erase(it); } + void remove(const QCborValue &key) + { iterator it = find(key); if (it != end()) erase(it); } + bool contains(qint64 key) const + { const_iterator it = find(key); return it != end(); } + bool contains(QLatin1String key) const + { const_iterator it = find(key); return it != end(); } + bool contains(const QString & key) const + { const_iterator it = find(key); return it != end(); } + bool contains(const QCborValue &key) const + { const_iterator it = find(key); return it != end(); } + + int compare(const QCborMap &other) const Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION; +#if QT_HAS_INCLUDE(<compare>) + std::strong_ordering operator<=>(const QCborMap &other) const + { + int c = compare(other); + if (c > 0) return std::strong_ordering::greater; + if (c == 0) return std::strong_ordering::equivalent; + return std::strong_ordering::less; + } +#else + bool operator==(const QCborMap &other) const Q_DECL_NOTHROW + { return compare(other) == 0; } + bool operator!=(const QCborMap &other) const Q_DECL_NOTHROW + { return !(*this == other); } + bool operator<(const QCborMap &other) const + { return compare(other) < 0; } +#endif + + typedef Iterator iterator; + typedef ConstIterator const_iterator; + iterator begin() { detach(); return iterator{d.data(), 1}; } + const_iterator constBegin() const { return const_iterator{d.data(), 1}; } + const_iterator begin() const { return constBegin(); } + const_iterator cbegin() const { return constBegin(); } + iterator end() { detach(); return iterator{d.data(), 2 * size() + 1}; } + const_iterator constEnd() const { return const_iterator{d.data(), 2 * size() + 1}; } + const_iterator end() const { return constEnd(); } + const_iterator cend() const { return constEnd(); } + iterator erase(iterator it); + iterator erase(const_iterator it) { return erase(iterator{ it.item.d, it.item.i }); } + bool empty() const { return isEmpty(); } + + iterator find(qint64 key); + iterator find(QLatin1String key); + iterator find(const QString & key); + iterator find(const QCborValue &key); + const_iterator constFind(qint64 key) const; + const_iterator constFind(QLatin1String key) const; + const_iterator constFind(const QString & key) const; + const_iterator constFind(const QCborValue &key) const; + const_iterator find(qint64 key) const { return constFind(key); } + const_iterator find(QLatin1String key) const { return constFind(key); } + const_iterator find(const QString & key) const { return constFind(key); } + const_iterator find(const QCborValue &key) const { return constFind(key); } + + iterator insert(qint64 key, const QCborValue &value) + { + QCborValueRef v = operator[](key); // detaches + v = value; + return { d.data(), v.i }; + } + iterator insert(QLatin1String key, const QCborValue &value) + { + QCborValueRef v = operator[](key); // detaches + v = value; + return { d.data(), v.i }; + } + iterator insert(const QString &key, const QCborValue &value) + { + QCborValueRef v = operator[](key); // detaches + v = value; + return { d.data(), v.i }; + } + iterator insert(const QCborValue &key, const QCborValue &value) + { + QCborValueRef v = operator[](key); // detaches + v = value; + return { d.data(), v.i }; + } + iterator insert(value_type v) { return insert(v.first, v.second); } + +private: + void detach(qsizetype reserve = 0); + + friend QCborValue; + explicit QCborMap(QCborContainerPrivate &dd) Q_DECL_NOTHROW; + QExplicitlySharedDataPointer<QCborContainerPrivate> d; +}; + +Q_DECLARE_SHARED(QCborMap) + +inline QCborMap QCborValueRef::toMap() const +{ + return concrete().toMap(); +} + +inline QCborMap QCborValueRef::toMap(const QCborMap &m) const +{ + return concrete().toMap(m); +} + +QT_END_NAMESPACE + +#endif // QCBORMAP_H diff --git a/src/corelib/serialization/qcborstream.h b/src/corelib/serialization/qcborstream.h index 3f0ce81915..22f6a8218e 100644 --- a/src/corelib/serialization/qcborstream.h +++ b/src/corelib/serialization/qcborstream.h @@ -244,6 +244,7 @@ private: } friend QCborStreamReaderPrivate; + friend class QCborContainerPrivate; quint64 value64; QScopedPointer<QCborStreamReaderPrivate> d; quint8 type_; diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp new file mode 100644 index 0000000000..c61a7f7eae --- /dev/null +++ b/src/corelib/serialization/qcborvalue.cpp @@ -0,0 +1,2550 @@ +/**************************************************************************** +** +** 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 "qcborvalue.h" +#include "qcborvalue_p.h" + +#include "qcborarray.h" +#include "qcbormap.h" +#include "qcborstream.h" + +#include <qendian.h> +#include <qlocale.h> +#include <private/qnumeric_p.h> +#include <qscopedvaluerollback.h> +#include <qstack.h> + +#include <new> + +QT_BEGIN_NAMESPACE + +/*! + \class QCborValue + \inmodule QtCore + \ingroup cbor + \reentrant + \since 5.12 + + \brief The QCborValue class encapsulates a value in CBOR. + + This class can be used to hold one of the many types available in CBOR. + CBOR is the Concise Binary Object Representation, a very compact form of + binary data encoding that is a superset of 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}. + + CBOR has three groups of built-in types: + + \list + \li Basic types: integers, floating point (double), boolean, null, etc. + \li String-like types: strings and byte arrays + \li Containers: arrays and maps + \endlist + + Additionally, CBOR supports a form of type extensibility by associating a + "tag" to one of the above types to convey more information. For example, a + UUID is represented by a tag and a byte array containing the 16 bytes of + the UUID content. QCborValue supports creating and decoding several of those + extended types directly with Qt classes (like QUuid). + + For the complete list, see \l QCborValue::Type. The type of a QCborValue can + be queried using type() or one of the "isXxxx" functions. + + \section1 Extended types and tagged values + + A tagged value is a normal QCborValue that is paired with a number that + is its tag. See \l QCborKnownTags for more information on what tags are in + the API as well as the full, official list. Such combinations form extended + types. + + QCborValue has support for certain extended types in the API, like URL + (with \l QUrl) and UUID (with \l QUuid). Other extended types not supported + in the API are represented by a QCborValue of \l {Type}{Tag} type. The tag + can later be retrieved by tag() and the tagged value using taggedValue(). + + In order to support future compatibility, QCborValues containing extended + Qt types compare equal to the tag type of the same contents. In other + words, the following expression is true: + + \code + QCborValue(uuid) == QCborValue(QCborKnownTags::Uuid, uuid.toRfc4122()); + \endcode + + \section1 Undefined and null values + + 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 + null QCborValue. + + Null values are used to indicate that an optional value is not present. In + that aspect, it is similar to the C++ Standard Library type \c + {std::optional} when that is disengaged. Unlike the C++ type, CBOR nulls + are simply of type "Null" and it is not possible to determine what concrete + type it is replacing. + + QCborValue can also be of the undefined type, which represents a value of + "undefined". In fact, that is what the QCborValue default constructor + creates. + + Undefined values are different from null values. While nulls are used to + indicate an optional value that is not provided, Undefined is usually + used to indicate that an expected value could not be provided, usually due + to an error or a precondition that could not be satisfied. + + 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. + + \section1 Simple types + + CBOR supports additional simple types that, like Null and Undefined, carry + no other value. They are called interchangeably "Simple Types" and "Simple + Values". CBOR encodes booleans as two distinct types (one for \c true and + one for \c false), but QCborValue has a convenience API for them. + + There are currently no other defined CBOR simple types. QCborValue supports + them simply by their number with API like isSimpleType() and + toSimpleType(), available for compatibility with future specifications + before the Qt API can be updated. Their use before such a specification is + discouraged, as other CBOR implementations may not support them fully. + + \section1 CBOR support + + QCborValue 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 QCborValue supports. + + \table + \header \li Feature \li Support + \row \li Unsigned numbers \li Yes (\l qint64 range) + \row \li Negative numbers \li Yes (\l qint64 range) + \row \li Byte strings \li Yes + \row \li Text strings \li Yes + \row \li Chunked strings \li See below + \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 + + Integers in QCborValue are limited to the range of the \l qint64 type. That + is, from -9,223,372,036,854,775,808 (-2\sup{63}) to + 9,223,372,036,854,775,807 (2\sup{63} - 1). CBOR itself can represent integer + values outside of this range, which QCborValue does not support. When + decoding a stream using fromCbor() containing one of those values, + QCborValue will convert automatically to \l {Type}{Double}, but that may + lose up to 11 bits of precision. + + fromCbor() is able to decode chunked strings, but will always merge the + chunks together into a single QCborValue. For that reason, it always writes + non-chunked strings when using toCbor() (which is required by the Canonical + format anyway). + + QCborValue will always convert half- and single-precision floating point + values in the CBOR stream to double-precision. The toCbor() function can + take a parameter indicating to recreate them. + + \section1 QCborValueRef + + QCborValueRef is a helper class for QCborArray and QCborMap. It is the type + you get when using one of the mutating APIs in those classes. Unlike + QCborValue, new values can be assigned to that class. When that is done, the + array or map it refers to will be modified with the new value. In all other + aspects, its API is identical to QCborValue. + + \sa QCborArray, QCborMap, QCborStreamReader, QCborStreamWriter + QJsonValue, QJsonDocument + */ + +/*! + \class QCborParserError + \inmodule QtCore + \ingroup cbor + \reentrant + \since 5.12 + + \brief The QCborParserError is used by QCborValue to report a parsing error. + + This class is used by \l {QCborValue::fromCbor(const QByteArray &ba, + QCborParserError *error)} to report a parser error and the byte offset + where the error was detected. + + \sa QCborValue, QCborError + */ + +/*! + \variable qint64 QCborParserError::offset + + This field contains the offset from the beginning of the data where the + error was detected. The offset should point to the beginning of the item + that contained the error, even if the error itself was elsewhere (for + example, for UTF-8 decoding issues). + + \sa QCborValue::fromCbor() + */ + +/*! + \variable QCborError QCborParserError::error + + This field contains the error code that indicates what decoding problem was + found. + + \sa QCborValue::fromCbor() + */ + +/*! + \fn QString QCborParserError::errorString() const + + Returns a string representation of the error code. This string is not + translated. + + \sa QCborError::toString(), QCborValue::fromCbor() + */ + +/*! + \enum QCborValue::EncodingOption + + This enum is used in the options argument to toCbor(), modifying the + behavior of the encoder. + + \omitvalue SortKeysInMaps + \value NoTransformation (Default) Performs no transformations. + \value UseFloat Tells the encoder to use IEEE 754 single-precision floating point + (that is, \c float) whenever possible. + \value UseFloat16 Tells the encoder to use IEEE 754 half-precision floating point + (that is, \c qfloat16), whenever possible. Implies \c UseFloat. + \value UseIntegers Tells the encoder to use integers whenever a value of type \l + {Type}{Double} contains an integer. + + The use of \c UseFloat16 is required to encode the stream in Canonical + Format, but is not otherwise necessary. + + \sa toCbor() + */ + +/*! + \enum QCborValue::DiagnosticNotationOption + + This enum is used in the option argument to toDiagnosticNotation(), to + modify the output format. + + \value Compact Does not use any line-breaks, producing a compact representation. + \value LineWrapped Uses line-breaks, one QCborValue per line. + \value ExtendedFormat Uses some different options to represent values, not found in + RFC 7049. Those options are subject to change. + + Currently, \c ExtendedFormat will change how byte arrays are represented. + Without it, they are always hex-encoded and without spaces. With it, + QCborValue::toCbor() will either use hex with spaces, base64 or base64url + encoding, depending on the context. + + \sa toDiagnosticNotation() + */ + +/*! + \enum QCborValue::Type + + This enum represents the QCborValue type. It is returned by the type() + function. + + The CBOR built-in types are: + + \value Integer \c qint64: An integer value + \value ByteArray \l QByteArray: a byte array ("byte string") + \value String \l QString: a Unicode string ("text string") + \value Array \l QCborArray: an array of QCborValues + \value Map \l QCborMap: an associative container of QCborValues + \value SimpleType \l QCborSimpleType: one of several simple types/values + \value False \c bool: the simple type for value \c false + \value True \c bool: the simple type for value \c true + \value Null \c std::nullptr_t: the simple type for the null value + \value Undefined (no type) the simple type for the undefined value + \value Double \c double: a double-precision floating point + \value Invalid Not a valid value, this usually indicates a CBOR decoding error + + Additionally, QCborValue can represent extended types: + + \value Tag An unknown or unrecognized extended type, represented by its + tag (a \l QCborTag) and the tagged value (a QCborValue) + \value DateTime \l QDateTime: a date and time stamp + \value Url \l QUrl: a URL or URI + \value RegularExpression \l QRegularExpression: the pattern of a regular expression + \value Uuid \l QUuid: a UUID + + \sa type() + */ + +/*! + \fn QCborValue::QCborValue() + + Creates a QCborValue of the \l {Type}{Undefined} type. + + CBOR undefined values are used to indicate missing information, usually as + a result of a previous operation that did not complete as expected. They + are also used by the QCborArray and QCborMap API to indicate the searched + item was not found. + + Undefined values are represented by the \l {QCborSimpleType}{Undefined + simple type}. Because of that, QCborValues with undefined values will also + return true for isSimpleType() and + \c{isSimpleType(QCborSimpleType::Undefined)}. + + Undefined values are different from null values. + + QCborValue objects with undefined values are also different from invalid + QCborValue objects. The API will not create invalid QCborValues, but they + may exist as a result of a parsing error. + + \sa isUndefined(), isNull(), isSimpleType() + */ + +/*! + \fn QCborValue::QCborValue(Type t_) + + Creates a QCborValue of type \a t_. The value associated with such a type + (if any) will be default constructed. + + \sa type() + */ + +/*! + \fn QCborValue::QCborValue(std::nullptr_t) + + Creates a QCborValue of the \l {Type}{Null} type. + + CBOR null values are used to indicate optional values that were not + provided. They are distinct from undefined values, in that null values are + usually not the result of an earlier error or problem. + + \sa isNull(), isUndefined(), isSimpleType() + */ + +/*! + \fn QCborValue::QCborValue(bool b) + + Creates a QCborValue with boolean value \a b. The value can later be + retrieved using toBool(). + + Internally, CBOR booleans are represented by a pair of types, one for true + and one for false. For that reason, boolean QCborValues will return true + for isSimpleType() and one of \c{isSimpleType(QCborSimpleType::False)} or + \c{isSimpleType(QCborSimpleType::True)}. + + \sa toBool(), isBool(), isTrue(), isFalse(), isSimpleType() + */ + +/*! + \fn QCborValue::QCborValue(qint64 i) + + Creates a QCborValue with integer value \a i. The value can later be + retrieved using toInteger(). + + CBOR integer values are distinct from floating point values. Therefore, + QCborValue objects with integers will compare differently to QCborValue + objects containing floating-point, even if the values contained in the + objects are equivalent. + + \sa toInteger(), isInteger(), isDouble() + */ + +/*! + \fn QCborValue::QCborValue(double d) + + Creates a QCborValue with floating point value \a d. The value can later be + retrieved using toDouble(). + + CBOR floating point values are distinct from integer values. Therefore, + QCborValue objects with integers will compare differently to QCborValue + objects containing floating-point, even if the values contained in the + objects are equivalent. + + \sa toDouble(), isDouble(), isInteger() + */ + +/*! + \fn QCborValue::QCborValue(QCborSimpleType st) + + Creates a QCborValue of simple type \a st. The type can later later be retrieved + 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. + + If \a st is \c{QCborSimpleType::Null}, the resulting QCborValue will be of + the \l{Type}{Null} type and similarly for \c{QCborSimpleType::Undefined}. + If \a st is \c{QCborSimpleType::False} or \c{QCborSimpleType::True}, the + created QCborValue will be a boolean containing a value of false or true, + respectively. + + This function can be used with simple types not defined in the API. For + example, to create a QCborValue with simple type 12, one could write: + + \code + QCborValue value(QCborSimpleType(12)); + \endcode + + Simple types should not be used until a specification for them has been + published, since other implementations may not support them properly. + Simple type values 24 to 31 are reserved and must not be used. + + isSimpleType(), isNull(), isUndefined(), isTrue(), isFalse() + */ + +/*! + \fn QCborValue::QCborValue(QCborKnownTags tag, const QCborValue &taggedValue) + \overload + + Creates a QCborValue for the extended type represented by the tag value \a + tag, tagging value \a taggedValue. The tag can later be retrieved using + tag() and the tagged value using taggedValue(). + + \sa isTag(), tag(), taggedValue(), QCborKnownTags + */ + +/*! + \fn QCborValue::~QCborValue() + + Disposes of the current QCborValue object and frees any associated resources. + */ + +/*! + \fn QCborValue::QCborValue(QCborValue &&other) + \overload + + Moves the contents of the \a other CBorValue object into this one and frees + the resources of this one. + */ + +/*! + \fn QCborValue &&QCborValue::operator=(QCborValue &&other) + \overload + + Moves the contents of the \a other CBorValue object into this one and frees + the resources of this one. Returns a reference to this object. + */ + +/*! + \fn void QCborValue::swap(QCborValue &other) + + Swaps the contents of this QCborValue object and \a other. + */ + +/*! + \fn QCborValue::Type QCborValue::type() const + + Returns the type of this QCborValue. The type can also later be retrieved by one + of the "isXxx" functions. + + \sa isInteger(), isByteArray(), isString(), isArray(), isMap(), + isTag(), isFalse(), isTrue(), isBool(), isNull(), isUndefined, isDouble(), + isDateTime(), isUrl(), isRegularExpression(), isUuid() + */ + +/*! + \fn bool QCborValue::isInteger() const + + Returns true if this QCborValue is of the integer type. The integer value + can be retrieved using toInteger(). + + \sa type(), toInteger() + */ + +/*! + \fn bool QCborValue::isByteArray() const + + Returns true if this QCborValue is of the byte array type. The byte array + value can be retrieved using toByteArray(). + + \sa type(), toByteArray() + */ + +/*! + \fn bool QCborValue::isString() const + + Returns true if this QCborValue is of the string type. The string value + can be retrieved using toString(). + + \sa type(), toString() + */ + +/*! + \fn bool QCborValue::isArray() const + + Returns true if this QCborValue is of the array type. The array value can + be retrieved using toArray(). + + \sa type(), toArray() + */ + +/*! + \fn bool QCborValue::isMap() const + + Returns true if this QCborValue is of the map type. The map value can be + retrieved using toMap(). + + \sa type(), toMap() + */ + +/*! + \fn bool QCborValue::isTag() const + + Returns true if this QCborValue is of the tag type. The tag value can be + retrieved using tag() and the tagged value using taggedValue(). + + This function does not return true for extended types that the API + recognizes. For code that handles extended types directly before the Qt API + is updated to support them, it is possible to recreate the tag + tagged + value pair by using reinterpretAsTag(). + + \sa type(), tag(), taggedValue(), reinterpretAsTag() + */ + +/*! + \fn bool QCborValue::isFalse() const + + Returns true if this QCborValue is a boolean with false value. This + function exists because, internally, CBOR booleans are stored as two + separate types, one for true and one for false. + + \sa type(), isBool(), isTrue(), toBool() + */ + +/*! + \fn bool QCborValue::isTrue() const + + Returns true if this QCborValue is a boolean with true value. This + function exists because, internally, CBOR booleans are stored as two + separate types, one for false and one for true. + + \sa type(), isBool(), isFalse(), toBool() + */ + +/*! + \fn bool QCborValue::isBool() const + + Returns true if this QCborValue is a boolean. The value can be retrieved + using toBool(). + + \sa type(), toBool(), isTrue(), isFalse() + */ + +/*! + \fn bool QCborValue::isUndefined() const + + Returns true if this QCborValue is of the undefined type. + + CBOR undefined values are used to indicate missing information, usually as + a result of a previous operation that did not complete as expected. They + are also used by the QCborArray and QCborMap API to indicate the searched + item was not found. + + Undefined values are distinct from null values. + + QCborValue objects with undefined values are also different from invalid + QCborValue objects. The API will not create invalid QCborValues, but they + may exist as a result of a parsing error. + + \sa type(), isNull(), isInvalid() + */ + +/*! + \fn bool QCborValue::isNull() const + + Returns true if this QCborValue is of the null type. + + CBOR null values are used to indicate optional values that were not + provided. They are distinct from undefined values, in that null values are + usually not the result of an earlier error or problem. + + Null values are distinct from undefined values and from invalid QCborValue + objects. The API will not create invalid QCborValues, but they may exist as + a result of a parsing error. + + \sa type(), isUndefined(), isInvalid() + */ + +/*! + \fn bool QCborValue::isDouble() const + + Returns true if this QCborValue is of the floating-point type. The value + can be retrieved using toDouble(). + + \sa type(), toDouble() + */ + +/*! + \fn bool QCborValue::isDateTime() const + + Returns true if this QCborValue is of the date/time type. The value can be + retrieved using toDateTime(). Date/times are extended types that use the + tag \l{QCborKnownTags}{DateTime}. + + Additionally, when decoding from a CBOR stream, QCborValue will interpret + tags of value \l{QCborKnownTags}{UnixTime_t} and convert them to the + equivalent date/time. + + \sa type(), toDateTime() + */ + +/*! + \fn bool QCborValue::isUrl() const + + Returns true if this QCborValue is of the URL type. The URL value + can be retrieved using toUrl(). + + \sa type(), toUrl() + */ + +/*! + \fn bool QCborValue::isRegularExpression() const + + Returns true if this QCborValue contains a regular expression's pattern. + The pattern can be retrieved using toRegularExpression(). + + \sa type(), toRegularExpression() + */ + +/*! + \fn bool QCborValue::isUuid() const + + Returns true if this QCborValue contains a UUID. The value can be retrieved + using toUuid(). + + \sa type(), toUuid() + */ + +/*! + \fn bool QCborValue::isInvalid() const + + Returns true if this QCborValue is not of any valid type. Invalid + QCborValues are distinct from those with undefined values and they usually + represent a decoding error. + + \sa isUndefined(), isNull() + */ + +/*! + \fn bool QCborValue::isContainer() const + + This convenience function returns true if the QCborValue is either an array + or a map. + + \sa isArray(), isMap() + */ + +/*! + \fn bool QCborValue::isSimpleType() const + + Returns true if this QCborValue is of one of the CBOR simple types. The + type itself can later be retrieved using type(), even for types that don't have an + enumeration in the API. They can also be checked with the + \l{isSimpleType(QCborSimpleType)} overload. + + \sa QCborSimpleType, isSimpleType(QCborSimpleType), toSimpleType() + */ + +/*! + \fn bool QCborValue::isSimpleType(QCborSimpleType st) const + \overload + + Returns true if this QCborValue is of a simple type and toSimpleType() + would return \a st, false otherwise. This function can be used to check for + any CBOR simple type, even those for which there is no enumeration in the + API. For example, for the simple type of value 12, you could write: + + \code + value.isSimpleType(QCborSimpleType(12)); + \endcode + + \sa QCborValue::QCborValue(QCborSimpleType), isSimpleType(), isFalse(), + isTrue(), isNull, isUndefined(), toSimpleType() + */ + +/*! + \fn QCborSimpleType QCborValue::toSimpleType(QCborSimpleType defaultValue) const + + Returns the simple type this QCborValue is of, if it is a simple type. If + it is not a simple type, it returns \a defaultValue. + + The following types are simple types and this function will return the + listed values: + + \table + \row \li QCborValue::False \li QCborSimpleType::False + \row \li QCborValue::True \li QCborSimpleType::True + \row \li QCborValue::Null \li QCborSimpleType::Null + \row \li QCborValue::Undefined \li QCborSimpleType::Undefined + \endtable + + \sa type(), isSimpleType(), isBool(), isTrue(), isFalse(), isTrue(), + isNull(), isUndefined() + */ + +/*! + \fn qint64 QCborValue::toInteger(qint64 defaultValue) const + + Returns the integer value stored in this QCborValue, if it is of the + integer type. If it is of the Double type, this function returns the + floating point value converted to integer. In any other case, it returns \a + defaultValue. + + \sa isInteger(), isDouble(), toDouble() + */ + +/*! + \fn bool QCborValue::toBool(bool defaultValue) const + + Returns the boolean value stored in this QCborValue, if it is of a boolean + type. Otherwise, it returns \a defaultValue. + + \sa isBool(), isTrue(), isFalse() + */ + +/*! + \fn double QCborValue::toDouble(double defaultValue) const + + Returns the floating point value stored in this QCborValue, if it is of the + Double type. If it is of the Integer type, this function returns the + integer value converted to double. In any other case, it returns \a + defaultValue. + + \sa isDouble(), isInteger(), toInteger() + */ + +using namespace QtCbor; + +// in qcborstream.cpp +extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error); + +/*! + Returns true if the double \a v can be converted to type \c T, false if + it's out of range. If the conversion is successful, the converted value is + stored in \a value; if it was not successful, \a value will contain the + minimum or maximum of T, depending on the sign of \a d. If \c T is + unsigned, then \a value contains the absolute value of \a v. + + This function works for v containing infinities, but not NaN. It's the + caller's responsibility to exclude that possibility before calling it. +*/ +template <typename T> static inline bool convertDoubleTo(double v, T *value) +{ + Q_STATIC_ASSERT(std::numeric_limits<T>::is_integer); + + // The [conv.fpint] (7.10 Floating-integral conversions) section of the C++ + // standard says only exact conversions are guaranteed. Converting + // integrals to floating-point with loss of precision has implementation- + // defined behavior whether the next higher or next lower is returned; + // converting FP to integral is UB if it can't be represented. + // + // That means we can't write UINT64_MAX+1. Writing ldexp(1, 64) would be + // correct, but only Clang, ICC and MSVC don't realize that it's a constant + // and the math call stays in the compiled code. + + double supremum; + if (std::numeric_limits<T>::is_signed) { + supremum = -1.0 * std::numeric_limits<T>::min(); // -1 * (-2^63) = 2^63, exact (for T = qint64) + *value = std::numeric_limits<T>::min(); + if (v < std::numeric_limits<T>::min()) + return false; + } else { + using ST = typename std::make_signed<T>::type; + supremum = -2.0 * std::numeric_limits<ST>::min(); // -2 * (-2^63) = 2^64, exact (for T = quint64) + v = fabs(v); + } + + *value = std::numeric_limits<T>::max(); + if (v >= supremum) + return false; + + // Now we can convert, these two conversions cannot be UB + *value = T(v); + +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wfloat-equal") +QT_WARNING_DISABLE_CLANG("-Wfloat-equal") + + return *value == v; + +QT_WARNING_POP +} + +static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::EncodingOptions opt) +{ + if (qt_is_nan(d)) { + if (opt & QCborValue::UseFloat16) { + if ((opt & QCborValue::UseFloat16) == QCborValue::UseFloat16) + return writer.append(qfloat16(qt_qnan())); + return writer.append(float(qt_qnan())); + } + return writer.append(qt_qnan()); + } + + if (qt_is_inf(d)) { + d = d > 0 ? qt_inf() : -qt_inf(); + } else if (opt & QCborValue::UseIntegers) { + quint64 i; + if (convertDoubleTo(d, &i)) { + if (d < 0) + return writer.append(QCborNegativeInteger(i)); + return writer.append(i); + } + } + + if (opt & QCborValue::UseFloat16) { + float f = float(d); + if (f == d) { + // no data loss, we could use float + if ((opt & QCborValue::UseFloat16) == QCborValue::UseFloat16) { + qfloat16 f16 = f; + if (f16 == f) + return writer.append(f16); + } + + return writer.append(f); + } + } + + writer.append(d); +} + +static inline int typeOrder(Element e1, Element e2) +{ + auto comparable = [](Element e) { + if (e.type >= 0x10000) + return QCborValue::Tag; + return e.type; + }; + return comparable(e1) - comparable(e2); +} + +namespace { +class DiagnosticNotation +{ +public: + static QString create(const QCborValue &v, QCborValue::DiagnosticNotationOptions opts) + { + DiagnosticNotation dn(opts); + return dn.createFromValue(v); + } + +private: + QStack<int> byteArrayFormatStack; + QCborValue::DiagnosticNotationOptions opts; + int nestingLevel = 0; + + DiagnosticNotation(QCborValue::DiagnosticNotationOptions opts_) + : opts(opts_) + { + byteArrayFormatStack.push(int(QCborKnownTags::ExpectedBase16)); + } + + QString createFromValue(const QCborValue &v); +}; +} + +QString DiagnosticNotation::createFromValue(const QCborValue &v) +{ + QString indent(1, QLatin1Char(' ')); + QString indented = indent; + if (opts & QCborValue::LineWrapped) { + indent = QLatin1Char('\n') + QString(4 * nestingLevel, QLatin1Char(' ')); + indented = indent + QLatin1String(" "); + } + QScopedValueRollback<int> rollback(nestingLevel); + ++nestingLevel; + + auto createFromArray = [=](const QCborArray &a) { + QString result; + QLatin1String comma; + for (auto v : a) { + result += comma + indented + createFromValue(v); + comma = QLatin1String(","); + } + return result; + }; + auto createFromMap = [=](const QCborMap &m) { + QString result; + QLatin1String comma; + for (auto v : m) { + result += comma + indented + createFromValue(v.first) + + QLatin1String(": ") + createFromValue(v.second); + comma = QLatin1String(","); + } + return result; + }; + auto makeFpString = [](double d) { + QString s; + quint64 v; + if (qt_is_inf(d)) { + s = (d < 0) ? QStringLiteral("-inf") : QStringLiteral("inf"); + } else if (qt_is_nan(d)) { + s = QStringLiteral("nan"); + } else if (convertDoubleTo(d, &v)) { + s = QString::fromLatin1("%1.0").arg(v); + if (d < 0) + s.prepend(QLatin1Char('-')); + } else { + s = QString::number(d, 'g', QLocale::FloatingPointShortest); + if (!s.contains(QLatin1Char('.')) && !s.contains('e')) + s += QLatin1Char('.'); + } + return s; + }; + auto isByteArrayEncodingTag = [](QCborTag tag) { + switch (quint64(tag)) { + case quint64(QCborKnownTags::ExpectedBase16): + case quint64(QCborKnownTags::ExpectedBase64): + case quint64(QCborKnownTags::ExpectedBase64url): + return true; + } + return false; + }; + + switch (v.type()) { + case QCborValue::Integer: + return QString::number(v.toInteger()); + case QCborValue::ByteArray: + switch (byteArrayFormatStack.top()) { + case int(QCborKnownTags::ExpectedBase16): + return QString::fromLatin1("h'" + + v.toByteArray().toHex(opts & QCborValue::ExtendedFormat ? ' ' : '\0') + + '\''); + case int(QCborKnownTags::ExpectedBase64): + return QString::fromLatin1("b64'" + v.toByteArray().toBase64() + '\''); + default: + case int(QCborKnownTags::ExpectedBase64url): + return QString::fromLatin1("b64'" + + v.toByteArray().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) + + '\''); + } + case QCborValue::String: + // ### TODO: Needs escaping! + return QLatin1Char('"') + v.toString() + QLatin1Char('"'); + case QCborValue::Array: + return QLatin1Char('[') + createFromArray(v.toArray()) + indent + QLatin1Char(']'); + case QCborValue::Map: + return QLatin1Char('{') + createFromMap(v.toMap()) + indent + QLatin1Char('}'); + case QCborValue::Tag: { + bool byteArrayFormat = opts & QCborValue::ExtendedFormat && isByteArrayEncodingTag(v.tag()); + if (byteArrayFormat) + byteArrayFormatStack.push(int(v.tag())); + QString result = QString::fromLatin1("%1(%2)").arg(quint64(v.tag())).arg(createFromValue(v.taggedValue())); + if (byteArrayFormat) + byteArrayFormatStack.pop(); + return result; + } + case QCborValue::SimpleType: + break; + case QCborValue::False: + return QStringLiteral("false"); + case QCborValue::True: + return QStringLiteral("true"); + case QCborValue::Null: + return QStringLiteral("null"); + case QCborValue::Undefined: + return QStringLiteral("undefined"); + case QCborValue::Double: + return makeFpString(v.toDouble()); + case QCborValue::DateTime: + case QCborValue::Url: + case QCborValue::RegularExpression: + case QCborValue::Uuid: + return createFromValue(v.reinterpretAsTag()); + case QCborValue::Invalid: + return QStringLiteral("<invalid>"); + } + + // must be a simple type + return QString::fromLatin1("simple(%1)").arg(quint8(v.toSimpleType())); +} + +QCborContainerPrivate::~QCborContainerPrivate() +{ + // delete our elements + for (Element &e : elements) { + if (e.flags & Element::IsContainer) + e.container->deref(); + } +} + +void QCborContainerPrivate::compact(qsizetype reserved) +{ + if (usedData > data.size() / 2) + return; + + // 50% savings if we recreate the byte data + // ### TBD + Q_UNUSED(reserved); +} + +QCborContainerPrivate *QCborContainerPrivate::clone(QCborContainerPrivate *d, qsizetype reserved) +{ + if (!d) { + d = new QCborContainerPrivate; + } else { + d = new QCborContainerPrivate(*d); + if (reserved >= 0) { + d->elements.reserve(reserved); + d->compact(reserved); + } + for (auto &e : qAsConst(d->elements)) { + if (e.flags & Element::IsContainer) + e.container->ref.ref(); + } + } + return d; +} + +QCborContainerPrivate *QCborContainerPrivate::detach(QCborContainerPrivate *d, qsizetype reserved) +{ + if (!d || d->ref.load() != 1) + return clone(d, reserved); + return d; +} + +void QCborContainerPrivate::replaceAt_complex(Element &e, const QCborValue &value) +{ + if (value.n < 0) { + // This QCborValue is an array, map, or tagged value (container points + // to itself). + + // detect self-assignment + if (Q_UNLIKELY(this == value.container)) { + Q_ASSERT(ref.load() >= 2); + QCborContainerPrivate *d = QCborContainerPrivate::clone(this); + d->elements.detach(); + e.container = d; + } else { + e.container = value.container; + } + + e.container->ref.ref(); + e.type = value.type(); + e.flags = Element::IsContainer; + } else { + // String data, copy contents + e = value.container->elements.at(value.n); + + // Copy string data, if any + if (const ByteData *b = value.container->byteData(value.n)) + e.value = addByteData(b->byte(), b->len); + } +} + +QT_WARNING_DISABLE_MSVC(4146) // unary minus operator applied to unsigned type, result still unsigned +static int compareContainer(const QCborContainerPrivate *c1, const QCborContainerPrivate *c2); +static int compareElementNoData(const Element &e1, const Element &e2) +{ + Q_ASSERT(e1.type == e2.type); + + if (e1.type == QCborValue::Integer) { + // CBOR sorting order is 0, 1, 2, ..., INT64_MAX, -1, -2, -3, ... INT64_MIN + // So we transform: + // 0 -> 0 + // 1 -> 1 + // INT64_MAX -> INT64_MAX + // -1 -> INT64_MAX + 1 = INT64_MAX - (-1) + // -2 -> INT64_MAX + 2 = INT64_MAX - (-2) + // INT64_MIN -> UINT64_MAX = INT64_MAX - INT64_MIN + // Note how the unsigned arithmethic is well defined in C++ (it's + // always performed modulo 2^64). + auto makeSortable = [](qint64 v) { + quint64 u = quint64(v); + if (v < 0) + return quint64(std::numeric_limits<qint64>::max()) + (-u); + return u; + }; + quint64 u1 = makeSortable(e1.value); + quint64 u2 = makeSortable(e2.value); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + } + + if (e1.type == QCborValue::Tag || e1.type == QCborValue::Double) { + // Perform unsigned comparisons for the tag value and floating point + quint64 u1 = quint64(e1.value); + quint64 u2 = quint64(e2.value); + if (u1 != u2) + return u1 < u2 ? -1 : 1; + } + + // Any other type is equal at this point: + // - simple types carry no value + // - empty strings, arrays and maps + return 0; +} + +static int compareElementRecursive(const QCborContainerPrivate *c1, const Element &e1, + const QCborContainerPrivate *c2, const Element &e2) +{ + int cmp = typeOrder(e1, e2); + if (cmp != 0) + return cmp; + + if ((e1.flags & Element::IsContainer) || (e2.flags & Element::IsContainer)) + return compareContainer(e1.flags & Element::IsContainer ? e1.container : nullptr, + e2.flags & Element::IsContainer ? e2.container : nullptr); + + // string data? + const ByteData *b1 = c1 ? c1->byteData(e1) : nullptr; + const ByteData *b2 = c2 ? c2->byteData(e2) : nullptr; + if (b1 || b2) { + auto len1 = b1 ? b1->len : 0; + auto len2 = b2 ? b2->len : 0; + + if (e1.flags & Element::StringIsUtf16) + len1 /= 2; + if (e2.flags & Element::StringIsUtf16) + len2 /= 2; + if (len1 == 0 || len2 == 0) + return len1 < len2 ? -1 : len1 == len2 ? 0 : 1; + + // we definitely have data from this point forward + Q_ASSERT(b1); + Q_ASSERT(b2); + + // Officially with CBOR, we sort first the string with the shortest + // UTF-8 length. The length of an ASCII string is the same as its UTF-8 + // and UTF-16 ones, but the UTF-8 length of a string is bigger than the + // UTF-16 equivalent. Combinations are: + // 1) UTF-16 and UTF-16 + // 2) UTF-16 and UTF-8 <=== this is the problem case + // 3) UTF-16 and US-ASCII + // 4) UTF-8 and UTF-8 + // 5) UTF-8 and US-ASCII + // 6) US-ASCII and US-ASCII + if ((e1.flags & Element::StringIsUtf16) && (e2.flags & Element::StringIsUtf16)) { + // Case 1: both UTF-16, so lengths are comparable. + // (we can't use memcmp in little-endian machines) + if (len1 == len2) + return QtPrivate::compareStrings(b1->asStringView(), b2->asStringView()); + return len1 < len2 ? -1 : 1; + } + + if (!(e1.flags & Element::StringIsUtf16) && !(e2.flags & Element::StringIsUtf16)) { + // Cases 4, 5 and 6: neither is UTF-16, so lengths are comparable too + // (this case includes byte arrays too) + if (len1 == len2) + return memcmp(b1->byte(), b2->byte(), size_t(len1)); + return len1 < len2 ? -1 : 1; + } + + if (!(e1.flags & Element::StringIsAscii) || !(e2.flags & Element::StringIsAscii)) { + // Case 2: one of them is UTF-8 and the other is UTF-16, so lengths + // are NOT comparable. We need to convert to UTF-16 first... + auto string = [](const Element &e, const ByteData *b) { + return e.flags & Element::StringIsUtf16 ? b->asQStringRaw() : b->toUtf8String(); + }; + + QString s1 = string(e1, b1); + QString s2 = string(e2, b2); + if (s1.size() == s2.size()) + return s1.compare(s2); + return s1.size() < s2.size() ? -1 : 1; + } + + // Case 3 (UTF-16 and US-ASCII) remains, so lengths are comparable again + if (len1 != len2) + return len1 < len2 ? -1 : 1; + if (e1.flags & Element::StringIsUtf16) + return QtPrivate::compareStrings(b1->asStringView(), b2->asLatin1()); + return QtPrivate::compareStrings(b1->asLatin1(), b2->asStringView()); + } + + return compareElementNoData(e1, e2); +} + +static int compareContainer(const QCborContainerPrivate *c1, const QCborContainerPrivate *c2) +{ + auto len1 = c1 ? c1->elements.size() : 0; + auto len2 = c2 ? c2->elements.size() : 0; + if (len1 != len2) { + // sort the shorter container first + return len1 < len2 ? -1 : 1; + } + + for (qsizetype i = 0; i < len1; ++i) { + const Element &e1 = c1->elements.at(i); + const Element &e2 = c2->elements.at(i); + int cmp = QCborContainerPrivate::compareElement_helper(c1, e1, c2, e2); + if (cmp) + return cmp; + } + + return 0; +} + +inline int QCborContainerPrivate::compareElement_helper(const QCborContainerPrivate *c1, Element e1, + const QCborContainerPrivate *c2, Element e2) +{ + return compareElementRecursive(c1, e1, c2, e2); +} + +/*! + \fn bool QCborValue::operator==(const QCborValue &other) const + + Compares this value and \a other, and returns true if they hold the same + contents, false otherwise. If each QCborValue contains an array or map, the + comparison is recursive to elements contained in them. + + For more information on CBOR equality in Qt, see, compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator!=(), operator<() + */ + +/*! + \fn bool QCborValue::operator!=(const QCborValue &other) const + + Compares this value and \a other, and returns true if contents differ, + false otherwise. If each QCborValue contains an array or map, the comparison + is recursive to elements contained in them. + + For more information on CBOR equality in Qt, see, QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator==(), operator<() + */ + +/*! + \fn bool QCborValue::operator<(const QCborValue &other) const + + Compares this value and \a other, and returns true if this value should be + sorted before \a other, false otherwise. If each QCborValue contains an + array or map, the comparison is recursive to elements contained in them. + + For more information on CBOR sorting order, see QCborValue::compare(). + + \sa compare(), QCborValue::operator==(), QCborMap::operator==(), + operator==(), operator!=() + */ + +/*! + Compares this value and \a other, and returns an integer that indicates + whether this value should be sorted prior to (if the result is negative) or + after \a other (if the result is positive). If this function returns 0, the + two values are equal and hold the same contents. + + If each QCborValue contains an array or map, the comparison is recursive to + elements contained in them. + + \section3 Extended types + + QCborValue compares equal a QCborValue containing an extended type, like + \l{Type}{Url} and \l{Type}{Url} and its equivalent tagged representation. + So, for example, the following expression is true: + + \code + QCborValue(QUrl("https://example.com")) == QCborValue(QCborKnownTags::Url, "https://example.com"); + \endcode + + Do note that Qt types like \l QUrl and \l QDateTime will normalize and + otherwise modify their arguments. The expression above is true only because + the string on the right side is the normalized value that the QCborValue on + the left would take. If, for example, the "https" part were uppercase in + both sides, the comparison would fail. For information on normalizations + performed by QCborValue, please consult the documentation of the + constructor taking the Qt type in question. + + \section3 Sorting order + + Sorting order in CBOR is defined in RFC 7049 + {https://tools.ietf.org/html/rfc7049#section-3.9}{section 3.9}, which + discusses the sorting of keys in a map when following the Canonical + encoding. According to the specification, "sorting is performed on the + bytes of the representation of the key data items" and lists as + consequences that: + + \list + \li "If two keys have different lengths, the shorter one sorts earlier;" + \li "If two keys have the same length, the one with the lower value in + (byte-wise) lexical order sorts earlier." + \endlist + + This results in surprising sorting of QCborValues, where the result of this + function is different from that which would later be retrieved by comparing the + contained elements. For example, the QCborValue containing string "zzz" + sorts before the QCborValue with string "foobar", even though when + comparing as \l{QString::compare()}{QStrings} or + \l{QByteArray}{QByteArrays} the "zzz" sorts after "foobar" + (dictionary order). + + The specification does not clearly indicate what sorting order should be + done for values of different types (it says sorting should not pay + "attention to the 3/5 bit splitting for major types"). QCborValue makes the + assumption that types should be sorted too. The numeric values of the + QCborValue::Type enumeration are in that order, with the exception of the + extended types, which compare as their tagged equivalents. + + \note Sorting order is preliminary and is subject to change. Applications + should not depend on the order returned by this function for the time + being. + + \sa QCborArray::compare(), QCborMap::compare(), operator==() + */ +int QCborValue::compare(const QCborValue &other) const +{ + Element e1 = QCborContainerPrivate::elementFromValue(*this); + Element e2 = QCborContainerPrivate::elementFromValue(other); + return compareElementRecursive(container, e1, other.container, e2); +} + +int QCborArray::compare(const QCborArray &other) const Q_DECL_NOTHROW +{ + return compareContainer(d.data(), other.d.data()); +} + +int QCborMap::compare(const QCborMap &other) const Q_DECL_NOTHROW +{ + return compareContainer(d.data(), other.d.data()); +} + +static void encodeToCbor(QCborStreamWriter &writer, const QCborContainerPrivate *d, qsizetype idx, + QCborValue::EncodingOptions opt) +{ + if (idx == -QCborValue::Array || idx == -QCborValue::Map) { + bool isArray = (idx == -QCborValue::Array); + qsizetype len = d ? d->elements.size() : 0; + if (isArray) + writer.startArray(quint64(len)); + else + writer.startMap(quint64(len) / 2); + + for (idx = 0; idx < len; ++idx) + encodeToCbor(writer, d, idx, opt); + + if (isArray) + writer.endArray(); + else + writer.endMap(); + } else if (idx < 0) { + if (d->elements.size() != 2) { + // invalid state! + qWarning("QCborValue: invalid tag state; are you encoding something that was improperly decoded?"); + return; + } + + // write the tag and the tagged element + writer.append(QCborTag(d->elements.at(0).value)); + encodeToCbor(writer, d, 1, opt); + } else { + // just one element + auto e = d->elements.at(idx); + const ByteData *b = d->byteData(idx); + switch (e.type) { + case QCborValue::Integer: + return writer.append(qint64(e.value)); + + case QCborValue::ByteArray: + if (b) + return writer.appendByteString(b->byte(), b->len); + return writer.appendByteString("", 0); + + case QCborValue::String: + if (b) { + if (e.flags & Element::StringIsUtf16) + return writer.append(b->asStringView()); + return writer.appendTextString(b->byte(), b->len); + } + return writer.append(QLatin1String()); + + case QCborValue::Array: + case QCborValue::Map: + case QCborValue::Tag: + // recurse + return encodeToCbor(writer, + e.flags & Element::IsContainer ? e.container : nullptr, + -qsizetype(e.type), opt); + + case QCborValue::SimpleType: + case QCborValue::False: + case QCborValue::True: + case QCborValue::Null: + case QCborValue::Undefined: + break; + + case QCborValue::Double: + return writeDoubleToCbor(writer, e.fpvalue(), opt); + + case QCborValue::Invalid: + return; + + case QCborValue::DateTime: + case QCborValue::Url: + case QCborValue::RegularExpression: + case QCborValue::Uuid: + // recurse as tag + return encodeToCbor(writer, e.container, -QCborValue::Tag, opt); + } + + // maybe it's a simple type + int simpleType = e.type - QCborValue::SimpleType; + if (unsigned(simpleType) < 0x100) + return writer.append(QCborSimpleType(simpleType)); + + // if we got here, we've got an unknown type + qWarning("QCborValue: found unknown type 0x%x", e.type); + } +} + +static inline double integerOutOfRange(const QCborStreamReader &reader) +{ + Q_ASSERT(reader.isInteger()); + if (reader.isUnsignedInteger()) { + quint64 v = reader.toUnsignedInteger(); + if (qint64(v) < 0) + return double(v); + } else { + quint64 v = quint64(reader.toNegativeInteger()); + if (qint64(v - 1) < 0) + return -double(v); + } + + // result is in range + return 0; +} + +static Element decodeBasicValueFromCbor(QCborStreamReader &reader) +{ + Element e = {}; + + switch (reader.type()) { + case QCborStreamReader::UnsignedInteger: + case QCborStreamReader::NegativeInteger: + if (double d = integerOutOfRange(reader)) { + e.type = QCborValue::Double; + qToUnaligned(d, &e.value); + } else { + e.type = QCborValue::Integer; + e.value = reader.toInteger(); + } + break; + case QCborStreamReader::SimpleType: + e.type = QCborValue::Type(quint8(reader.toSimpleType()) + 0x100); + break; + case QCborStreamReader::Float16: + e.type = QCborValue::Double; + qToUnaligned(double(reader.toFloat16()), &e.value); + break; + case QCborStreamReader::Float: + e.type = QCborValue::Double; + qToUnaligned(double(reader.toFloat()), &e.value); + break; + case QCborStreamReader::Double: + e.type = QCborValue::Double; + qToUnaligned(reader.toDouble(), &e.value); + break; + + default: + Q_UNREACHABLE(); + } + + reader.next(); + return e; +} + +static inline QCborContainerPrivate *createContainerFromCbor(QCborStreamReader &reader) +{ + auto d = new QCborContainerPrivate; + d->ref.store(1); + d->decodeFromCbor(reader); + return d; +} + +static QCborValue taggedValueFromCbor(QCborStreamReader &reader) +{ + auto d = new QCborContainerPrivate; + d->append(reader.toTag()); + reader.next(); + + if (reader.lastError() == QCborError::NoError) { + // decode tagged value + d->decodeValueFromCbor(reader); + } + + QCborValue::Type type = QCborValue::Tag; + if (reader.lastError() == QCborError::NoError) { + // post-process to create our extended types + qint64 tag = d->elements.at(0).value; + auto &e = d->elements[1]; + const ByteData *b = d->byteData(e); + + auto replaceByteData = [&](const char *buf, qsizetype len) { + d->data.clear(); + d->usedData = 0; + e.flags = Element::HasByteData | Element::StringIsAscii; + e.value = d->addByteData(buf, len); + }; + + switch (tag) { + case qint64(QCborKnownTags::DateTimeString): + case qint64(QCborKnownTags::UnixTime_t): { + QDateTime dt; + if (tag == qint64(QCborKnownTags::DateTimeString) && b && + e.type == QCborValue::String && (e.flags & Element::StringIsUtf16) == 0) { + // The data is supposed to be US-ASCII. If it isn't, + // QDateTime::fromString will fail anyway. + dt = QDateTime::fromString(b->asLatin1(), Qt::ISODateWithMs); + } else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Integer) { + dt = QDateTime::fromSecsSinceEpoch(e.value, Qt::UTC); + } else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Double) { + dt = QDateTime::fromMSecsSinceEpoch(qint64(e.fpvalue() * 1000), Qt::UTC); + } + if (dt.isValid()) { + QByteArray text = dt.toString(Qt::ISODateWithMs).toLatin1(); + replaceByteData(text, text.size()); + e.type = QCborValue::String; + d->elements[0].value = qint64(QCborKnownTags::DateTimeString); + type = QCborValue::DateTime; + } + break; + } + + case qint64(QCborKnownTags::Url): + if (e.type == QCborValue::String) { + if (b) { + // normalize to a short (decoded) form, so as to save space + QUrl url(e.flags & Element::StringIsUtf16 ? + b->asQStringRaw() : + b->toUtf8String()); + QByteArray encoded = url.toString(QUrl::DecodeReserved).toUtf8(); + replaceByteData(encoded, encoded.size()); + } + type = QCborValue::Url; + } + break; + + case quint64(QCborKnownTags::RegularExpression): + if (e.type == QCborValue::String) { + // no normalization is necessary + type = QCborValue::RegularExpression; + } + break; + + case qint64(QCborKnownTags::Uuid): + if (e.type == QCborValue::ByteArray) { + // force the size to 16 + char buf[sizeof(QUuid)] = {}; + if (b) + memcpy(buf, b->byte(), qMin(sizeof(buf), size_t(b->len))); + replaceByteData(buf, sizeof(buf)); + + type = QCborValue::Uuid; + } + break; + } + } else { + // decoding error + type = QCborValue::Invalid; + } + + // note: may return invalid state! + return QCborContainerPrivate::makeValue(type, -1, d); +} + +void QCborContainerPrivate::decodeStringFromCbor(QCborStreamReader &reader) +{ + auto addByteData_local = [this](QByteArray::size_type len) -> qint64 { + // this duplicates a lot of addByteData, but with overflow checking + QByteArray::size_type newSize; + QByteArray::size_type increment = sizeof(QtCbor::ByteData); + QByteArray::size_type alignment = alignof(QtCbor::ByteData); + QByteArray::size_type offset = data.size(); + + // calculate the increment we want + if (add_overflow(increment, len, &increment)) + return -1; + + // align offset + if (add_overflow(offset, alignment - 1, &offset)) + return -1; + offset &= ~(alignment - 1); + + // and calculate the final size + if (add_overflow(offset, increment, &newSize)) + return -1; + + // since usedData <= data.size(), this can't overflow + usedData += increment; + data.resize(newSize); + return offset; + }; + auto dataPtr = [this]() { + // Null happens when we're reading zero bytes. + Q_ASSERT(data.isNull() || data.isDetached()); + return const_cast<char *>(data.constData()); + }; + + Element e = {}; + e.type = (reader.isByteArray() ? QCborValue::ByteArray : QCborValue::String); + if (reader.lastError() != QCborError::NoError) + return; + + qsizetype rawlen = reader.currentStringChunkSize(); + QByteArray::size_type len = rawlen; + if (rawlen < 0) + return; // error + if (len != rawlen) { + // truncation + qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge }); + return; + } + + // allocate space, but only if there will be data + if (len != 0 || !reader.isLengthKnown()) { + e.flags = Element::HasByteData; + e.value = addByteData_local(len); + if (e.value < 0) { + // overflow + qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge }); + return; + } + } + + // read chunks + bool isAscii = (e.type == QCborValue::String); + auto r = reader.readStringChunk(dataPtr() + e.value + sizeof(ByteData), len); + while (r.status == QCborStreamReader::Ok) { + if (e.type == QCborValue::String && len) { + // verify UTF-8 string validity + auto utf8result = QUtf8::isValidUtf8(dataPtr() + data.size() - len, len); + if (!utf8result.isValidUtf8) { + r.status = QCborStreamReader::Error; + qt_cbor_stream_set_error(reader.d.data(), { QCborError::InvalidUtf8String }); + break; + } + isAscii = isAscii && utf8result.isValidAscii; + } + + // allocate space for the next chunk + rawlen = reader.currentStringChunkSize(); + len = rawlen; + if (len == rawlen) { + auto oldSize = data.size(); + auto newSize = oldSize; + if (!add_overflow(newSize, len, &newSize)) { + if (newSize != oldSize) + data.resize(newSize); + + // read the chunk + r = reader.readStringChunk(dataPtr() + oldSize, len); + continue; + } + } + + // error + r.status = QCborStreamReader::Error; + qt_cbor_stream_set_error(reader.d.data(), { QCborError::DataTooLarge }); + } + + if (r.status == QCborStreamReader::Error) { + // There can only be errors if there was data to be read. + Q_ASSERT(e.flags & Element::HasByteData); + data.truncate(e.value); + return; + } + + // update size + if (e.flags & Element::HasByteData) { + auto b = new (dataPtr() + e.value) ByteData; + b->len = data.size() - e.value - int(sizeof(*b)); + usedData += b->len; + + if (isAscii) { + // set the flag if it is US-ASCII only (as it often is) + Q_ASSERT(e.type == QCborValue::String); + e.flags |= Element::StringIsAscii; + } + } + + elements.append(e); +} + +void QCborContainerPrivate::decodeValueFromCbor(QCborStreamReader &reader) +{ + switch (reader.type()) { + case QCborStreamReader::UnsignedInteger: + case QCborStreamReader::NegativeInteger: + case QCborStreamReader::SimpleType: + case QCborStreamReader::Float16: + case QCborStreamReader::Float: + case QCborStreamReader::Double: + elements.append(decodeBasicValueFromCbor(reader)); + break; + + case QCborStreamReader::ByteArray: + case QCborStreamReader::String: + decodeStringFromCbor(reader); + break; + + case QCborStreamReader::Array: + case QCborStreamReader::Map: + case QCborStreamReader::Tag: + return append(QCborValue::fromCbor(reader)); + + case QCborStreamReader::Invalid: + return; // probably a decode error + } +} + +void QCborContainerPrivate::decodeFromCbor(QCborStreamReader &reader) +{ + int mapShift = reader.isMap() ? 1 : 0; + if (reader.isLengthKnown()) { + quint64 len = reader.length(); + + // Clamp allocation to 1M elements (avoids crashing due to corrupt + // stream or loss of precision when converting from quint64 to + // QVector::size_type). + len = qMin(len, quint64(1024 * 1024 - 1)); + elements.reserve(qsizetype(len) << mapShift); + } + + reader.enterContainer(); + if (reader.lastError() != QCborError::NoError) + return; + + while (reader.hasNext() && reader.lastError() == QCborError::NoError) + decodeValueFromCbor(reader); + + if (reader.lastError() == QCborError::NoError) + reader.leaveContainer(); +} + +/*! + Creates a QCborValue with byte array value \a ba. The value can later be + retrieved using toByteArray(). + + \sa toByteArray(), isByteArray(), isString() + */ +QCborValue::QCborValue(const QByteArray &ba) + : n(0), container(new QCborContainerPrivate), t(ByteArray) +{ + container->appendByteData(ba.constData(), ba.size(), t); + container->ref.store(1); +} + +/*! + Creates a QCborValue with string value \a s. The value can later be + retrieved using toString(). + + \sa toString(), isString(), isByteArray() + */ +QCborValue::QCborValue(const QString &s) + : n(0), container(new QCborContainerPrivate), t(String) +{ + container->append(s); + container->ref.store(1); +} + +/*! + \overload + + Creates a QCborValue with string value \a s. The value can later be + retrieved using toString(). + + \sa toString(), isString(), isByteArray() + */ +QCborValue::QCborValue(QLatin1String s) + : n(0), container(new QCborContainerPrivate), t(String) +{ + container->append(s); + container->ref.store(1); +} + +/*! + Creates a QCborValue with the array \a a. The array can later be retrieved + using toArray(). + + \sa toArray(), isArray(), isMap() + */ +QCborValue::QCborValue(const QCborArray &a) + : n(-1), container(a.d.data()), t(Array) +{ + if (container) + container->ref.ref(); +} + +/*! + Creates a QCborValue with the map \a m. The map can later be retrieved + using toMap(). + + \sa toMap(), isMap(), isArray() + */ +QCborValue::QCborValue(const QCborMap &m) + : n(-1), container(m.d.data()), t(Map) +{ + if (container) + container->ref.ref(); +} + +/*! + Creates a QCborValue for the extended type represented by the tag value \a + tag, tagging value \a tv. The tag can later be retrieved using tag() and + the tagged value using taggedValue(). + + \sa isTag(), tag(), taggedValue(), QCborKnownTags + */ +QCborValue::QCborValue(QCborTag tag, const QCborValue &tv) + : n(-1), container(new QCborContainerPrivate), t(Tag) +{ + container->ref.store(1); + container->append(tag); + container->append(tv); +} + +/*! + Copies the contents of \a other into this object. + */ +QCborValue::QCborValue(const QCborValue &other) + : n(other.n), container(other.container), t(other.t) +{ + if (container) + container->ref.ref(); +} + +/*! + Creates a QCborValue object of the date/time extended type and containing + the value represented by \a dt. The value can later be retrieved using + toDateTime(). + + The CBOR date/time types are extension types using tags: either a string + (in ISO date format) tagged as a \l{QCborKnownTags}{DateTime} or a number + (of seconds since the start of 1970, UTC) tagged as a + \l{QCborKnownTags}{UnixTime_t}. When parsing CBOR streams, QCborValue will + convert \l{QCborKnownTags}{UnixTime_t} to the string-based type. + + \sa toDateTime(), isDateTime(), reinterpretAsTag() + */ +QCborValue::QCborValue(const QDateTime &dt) + : QCborValue(QCborKnownTags::DateTimeString, dt.toString(Qt::ISODateWithMs).toLatin1()) +{ + // change types + t = DateTime; + container->elements[1].type = String; +} + +/*! + 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(). + + The CBOR URL type is an extended type represented by a string tagged as an + \l{QCborKnownTags}{Url}. + + \sa toUrl(), isUrl(), reinterpretAsTag() + */ +QCborValue::QCborValue(const QUrl &url) + : QCborValue(QCborKnownTags::Url, url.toString(QUrl::DecodeReserved).toUtf8()) +{ + // change types + t = Url; + container->elements[1].type = String; +} + +/*! + Creates a QCborValue object of the regular expression pattern extended type + and containing the value represented by \a rx. The value can later be retrieved + using toRegularExpression(). + + The CBOR regular expression type is an extended type represented by a + string tagged as an \l{QCborKnownTags}{RegularExpression}. Note that CBOR + regular expressions only store the patterns, so any flags that the + QRegularExpression object may carry will be lost. + + \sa toRegularExpression(), isRegularExpression(), reinterpretAsTag() + */ +QCborValue::QCborValue(const QRegularExpression &rx) + : QCborValue(QCborKnownTags::RegularExpression, rx.pattern()) +{ + // change type + t = RegularExpression; +} + +/*! + Creates a QCborValue object of the UUID extended type and containing the + value represented by \a uuid. The value can later be retrieved using + toUuid(). + + The CBOR UUID type is an extended type represented by a byte array tagged + as an \l{QCborKnownTags}{Uuid}. + + \sa toUuid(), isUuid(), reinterpretAsTag() + */ +QCborValue::QCborValue(const QUuid &uuid) + : QCborValue(QCborKnownTags::Uuid, uuid.toRfc4122()) +{ + // change our type + t = Uuid; +} + +// destructor +void QCborValue::dispose() +{ + container->deref(); +} + +/*! + Replaces the contents of this QCborObject with a copy of \a other. + */ +QCborValue &QCborValue::operator=(const QCborValue &other) +{ + if (other.container) + other.container->ref.ref(); + if (container) + container->deref(); + + n = other.n; + container = other.container; + t = other.t; + return *this; +} + +/*! + Returns the tag of this extended QCborValue object, if it is of the tag + type, \a defaultValue otherwise. + + CBOR represents extended types by associating a number (the tag) with a + stored representation. This function returns that number. To retrieve the + representation, use taggedValue(). + + This function does not directly return the tag associated with extended + types. In order to do that, first convert the extended type to tag type + using reinterpretAsTag(). + + \sa isTag(), taggedValue(), reinterpretAsTag(), + isDateTime(), isUrl(), isRegularExpression(), isUuid() + */ +QCborTag QCborValue::tag(QCborTag defaultValue) const +{ + return isTag() && container && container->elements.size() == 2 ? + QCborTag(container->elements.at(0).value) : defaultValue; +} + +/*! + Returns the tagged value of this extended QCborValue object, if it is of + the tag type, \a defaultValue otherwise. + + CBOR represents extended types by associating a number (the tag) with a + stored representation. This function returns that representation. To + retrieve the tag, use tag(). + + This function does not directly return the representation associated with + extended types. In order to do that, first convert the extended type to tag + type using reinterpretAsTag(). + + \sa isTag(), tag(), reinterpretAsTag(), + isDateTime(), isUrl(), isRegularExpression(), isUuid() + */ +QCborValue QCborValue::taggedValue(const QCborValue &defaultValue) const +{ + return isTag() && container && container->elements.size() == 2 ? + container->valueAt(1) : defaultValue; +} + +/*! + Returns the equivalent representation of a QCborValue extended type, in the + form of a tag object. If this object is not an extended type, this function + returns an invalid QCborValue object (not undefined). + + \sa isTag(), tag(), taggedValue(), isInvalid(), + isDateTime(), isUrl(), isRegularExpression(), isUuid() + */ +QCborValue QCborValue::reinterpretAsTag() const +{ + QCborValue result = *this; + if (t >= 0x10000) + result.t = Tag; + else + result.t = Invalid; + return result; +} + +/*! + Returns the byte array value stored in this QCborValue, if it is of the byte + array type. Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to + QByteArray. + + \sa isByteArray(), isString(), toString() + */ +QByteArray QCborValue::toByteArray(const QByteArray &defaultValue) const +{ + if (!container || !isByteArray()) + return defaultValue; + + Q_ASSERT(n >= 0); + return container->byteArrayAt(n); +} + +/*! + Returns the string value stored in this QCborValue, if it is of the string + type. Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to + QString. + + \sa isString(), isByteArray(), toByteArray() + */ +QString QCborValue::toString(const QString &defaultValue) const +{ + if (!container || !isString()) + return defaultValue; + + Q_ASSERT(n >= 0); + return container->stringAt(n); +} + +/*! + Returns the date/time value stored in this QCborValue, if it is of the + date/time extended type. Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to + QDateTime. + + \sa isDateTime(), isTag(), taggedValue() + */ +QDateTime QCborValue::toDateTime(const QDateTime &defaultValue) const +{ + if (!container || !isDateTime() || container->elements.size() != 2) + return defaultValue; + + Q_ASSERT(n == -1); + const ByteData *byteData = container->byteData(1); + if (!byteData) + return defaultValue; // date/times are never empty, so this must be invalid + + // Our data must be US-ASCII. + Q_ASSERT((container->elements.at(1).flags & Element::StringIsUtf16) == 0); + return QDateTime::fromString(byteData->asLatin1(), Qt::ISODateWithMs); +} + +/*! + Returns the URL value stored in this QCborValue, if it is of the URL + extended type. Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to QUrl. + + \sa isUrl(), isTag(), taggedValue() + */ +QUrl QCborValue::toUrl(const QUrl &defaultValue) const +{ + if (!container || !isUrl() || container->elements.size() != 2) + return defaultValue; + + Q_ASSERT(n == -1); + const ByteData *byteData = container->byteData(1); + if (!byteData) + return QUrl(); // valid, empty URL + + return QUrl::fromEncoded(byteData->asByteArrayView()); +} + +/*! + Returns the regular expression value stored in this QCborValue, if it is of + the regular expression pattern extended type. Otherwise, it returns \a + defaultValue. + + Note that this function performs no conversion from other types to + QRegularExpression. + + \sa isRegularExpression(), isTag(), taggedValue() + */ +QRegularExpression QCborValue::toRegularExpression(const QRegularExpression &defaultValue) const +{ + if (!container || !isRegularExpression() || container->elements.size() != 2) + return defaultValue; + + Q_ASSERT(n == -1); + return QRegularExpression(container->stringAt(1)); +} + +/*! + Returns the UUID value stored in this QCborValue, if it is of the UUID + extended type. Otherwise, it returns \a defaultValue. + + Note that this function performs no conversion from other types to QUuid. + + \sa isUuid(), isTag(), taggedValue() + */ +QUuid QCborValue::toUuid(const QUuid &defaultValue) const +{ + if (!container || !isUuid() || container->elements.size() != 2) + return defaultValue; + + Q_ASSERT(n == -1); + const ByteData *byteData = container->byteData(1); + if (!byteData) + return defaultValue; // UUIDs must always be 16 bytes, so this must be invalid + + return QUuid::fromRfc4122(byteData->asByteArrayView()); +} + +QCborArray QCborValue::toArray() const +{ + return toArray(QCborArray()); +} + +/*! + 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() + */ +QCborArray QCborValue::toArray(const QCborArray &defaultValue) const +{ + if (!isArray()) + return defaultValue; + QCborContainerPrivate *dd = nullptr; + Q_ASSERT(n == -1 || container == nullptr); + if (n < 0) + dd = container; + return dd ? QCborArray(*dd) : defaultValue; +} + +QCborMap QCborValue::toMap() const +{ + return toMap(QCborMap()); +} + +/*! + 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() + */ +QCborMap QCborValue::toMap(const QCborMap &defaultValue) const +{ + if (!isMap()) + return defaultValue; + QCborContainerPrivate *dd = nullptr; + Q_ASSERT(n == -1 || container == nullptr); + if (n < 0) + dd = container; + return dd ? QCborMap(*dd) : defaultValue; +} + +/*! + If this QCborValue is 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 + QCborValue 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 QCborValue::operator[](const QString &key) const +{ + if (isMap()) + return toMap().value(key); + return QCborValue(); +} + +/*! + \overload + + If this QCborValue is 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 + QCborValue 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 QCborValue::operator[](QLatin1String key) const +{ + if (isMap()) + return toMap().value(key); + return QCborValue(); +} + +/*! + 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 + 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()) + return toMap().value(key); + if (isArray()) + return toArray().at(key); + return QCborValue(); +} + +/*! + 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 + outermost object is finished. + + This function need not be used on the root element of a \l + QCborStreamReader. For example, the following code illustrates how to skip + the CBOR signature tag from the beginning of a file: + + \code + if (reader.isTag() && reader.toTag() == QCborKnownTags::Signature) + reader.next(); + + QCborValue contents = QCborValue::fromCbor(reader); + \endcode + + The returned value may be partially complete and indistinguishable from a + valid QCborValue even if the decoding failed. To determine if there was an + error, check if \l{QCborStreamReader::lastError()}{reader.lastError()} is + indicating an error condition. This function stops decoding immediately + after the first error. + + \sa toCbor(), toDiagnosticNotation(), toVariant(), toJsonValue() + */ +QCborValue QCborValue::fromCbor(QCborStreamReader &reader) +{ + QCborValue result; + auto t = reader.type(); + if (reader.lastError() != QCborError::NoError) + t = QCborStreamReader::Invalid; + + switch (t) { + // basic types, no container needed: + case QCborStreamReader::UnsignedInteger: + case QCborStreamReader::NegativeInteger: + case QCborStreamReader::SimpleType: + case QCborStreamReader::Float16: + case QCborStreamReader::Float: + case QCborStreamReader::Double: { + Element e = decodeBasicValueFromCbor(reader); + result.n = e.value; + result.t = e.type; + break; + } + + case QCborStreamReader::Invalid: + result.t = QCborValue::Invalid; + break; // probably a decode error + + // strings + case QCborStreamReader::ByteArray: + case QCborStreamReader::String: + result.n = 0; + result.t = reader.isString() ? String : ByteArray; + result.container = new QCborContainerPrivate; + result.container->ref.ref(); + result.container->decodeStringFromCbor(reader); + break; + + // containers + case QCborStreamReader::Array: + case QCborStreamReader::Map: + result.n = -1; + result.t = reader.isArray() ? Array : Map; + result.container = createContainerFromCbor(reader); + break; + + // tag + case QCborStreamReader::Tag: + result = taggedValueFromCbor(reader); + break; + } + + return result; +} + +/*! + \overload + + Decodes one item from the CBOR stream found in the byte array \a ba 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 outermost object is finished. + + This function stores the error state, if any, in the object pointed to by + \a error, along with the offset of where the error occurred. If no error + happened, it stores \l{QCborError}{NoError} in the error state and the + number of bytes that it consumed (that is, it stores the offset for the + first unused byte). Using that information makes it possible to parse + further data that may exist in the same byte array. + + The returned value may be partially complete and indistinguishable from a + valid QCborValue even if the decoding failed. To determine if there was an + error, check if there was an error stored in \a error. This function stops + decoding immediately after the first error. + + \sa toCbor(), toDiagnosticNotation(), toVariant(), toJsonValue() + */ +QCborValue QCborValue::fromCbor(const QByteArray &ba, QCborParserError *error) +{ + QCborStreamReader reader(ba); + QCborValue result = fromCbor(reader); + if (error) { + error->error = reader.lastError(); + error->offset = reader.currentOffset(); + } + return result; +} + +/*! + Encodes this QCborValue object to its CBOR representation, using the + options specified in \a opt, and return the byte array containing that + representation. + + This function will not fail, except if this QCborValue or any of the + contained items, if this is a map or array, are invalid. Invalid types are + not produced normally by the API, but can result from decoding errors. + + By default, this function performs no transformation on the values in the + QCborValue, writing all floating point directly as double-precision (\c + double) types. If the \l{EncodingOption}{UseFloat} option is specified, it + will use single precision (\c float) for any floating point value for which + there's no loss of precision in using that representation. That includes + infinities and NaN values. + + Similarly, if \l{EncodingOption}{UseFloat16} is specified, this function + will try to use half-precision (\l qfloat16) floating point if the + conversion to that results in no loss of precision. This is always true for + infinities and NaN. + + If \l{EncodingOption}{UseIntegers} is specified, it will use integers for + any floating point value that contains an actual integer. + + \sa fromCbor(), fromVariant(), fromJsonValue() + */ +QByteArray QCborValue::toCbor(EncodingOptions opt) +{ + QByteArray result; + QCborStreamWriter writer(&result); + toCbor(writer, opt); + return result; +} + +/*! + \overload + + Encodes this QCborValue object to its CBOR representation, using the + options specified in \a opt, to the writer specified by \a writer. The same + writer can be used by multiple QCborValues, for example, in order to encode + different elements in a larger array. + + This function will not fail, except if this QCborValue or any of the + contained items, if this is a map or array, are invalid. Invalid types are + not produced normally by the API, but can result from decoding errors. + + By default, this function performs no transformation on the values in the + QCborValue, writing all floating point directly as double-precision + (binary64) types. If the \l{EncodingOption}{UseFloat} option is + specified, it will use single precision (binary32) for any floating point + value for which there's no loss of precision in using that representation. + That includes infinities and NaN values. + + Similarly, if \l{EncodingOption}{UseFloat16} is specified, this function + will try to use half-precision (binary16) floating point if the conversion + to that results in no loss of precision. This is always true for infinities + and NaN. + + If \l{EncodingOption}{UseIntegers} is specified, it will use integers + for any floating point value that contains an actual integer. + + \sa fromCbor(), fromVariant(), fromJsonValue() + */ +Q_NEVER_INLINE void QCborValue::toCbor(QCborStreamWriter &writer, EncodingOptions opt) +{ + if (isContainer() || isTag()) + return encodeToCbor(writer, container, -type(), opt); + if (container) + return encodeToCbor(writer, container, n, opt); + + // very simple types + if (isSimpleType()) + return writer.append(toSimpleType()); + + switch (type()) { + case Integer: + return writer.append(n); + + case Double: + return writeDoubleToCbor(writer, fp_helper(), opt); + + case Invalid: + return; + + case SimpleType: + case False: + case True: + case Null: + case Undefined: + // handled by "if (isSimpleType())" + Q_UNREACHABLE(); + break; + + case ByteArray: + // Byte array with no container is empty + return writer.appendByteString("", 0); + + case String: + // String with no container is empty + return writer.appendTextString("", 0); + + case Array: + case Map: + case Tag: + // handled by "if (isContainer() || isTag())" + Q_UNREACHABLE(); + break; + + case DateTime: + case Url: + case RegularExpression: + case Uuid: + // not possible + Q_UNREACHABLE(); + break; + } +} + +/*! + Creates the diagnostic notation equivalent of this CBOR object and return + it. The \a opts parameter controls the dialect of the notation. Diagnostic + notation is useful in debugging, to aid the developer in understanding what + value is stored in the QCborValue or in a CBOR stream. For that reason, the + Qt API provides no support for parsing the diagnostic back into the + in-memory format or CBOR stream, though the representation is unique and it + would be possible. + + CBOR diagnostic notation is specified by + \l{https://tools.ietf.org/html/rfc7049#section-6}{section 6} of RFC 7049. + It is a text representation of the CBOR stream and it is very similar to + JSON, but it supports the CBOR types not found in JSON. The extended format + enabled by the \l{DiagnosticNotationOption}{ExtendedFormat} flag is + currently in some IETF drafts and its format is subject to change. + + This function produces the equivalent representation of the stream that + toCbor() would produce, without any transformation option provided there. + This also implies this function may not produce a representation of the + stream that was used to create the object, if it was created using + fromCbor(), as that function may have applied transformations. For a + high-fidelity notation of a stream, without transformation, see the \c + cbordump example. + + \sa toCbor(), toJsonDocument(), QJsonDocument::toJson() + */ +QString QCborValue::toDiagnosticNotation(DiagnosticNotationOptions opts) const +{ + return DiagnosticNotation::create(*this, opts); +} + +void QCborValueRef::toCbor(QCborStreamWriter &writer, QCborValue::EncodingOptions opt) +{ + concrete().toCbor(writer, opt); +} + +QCborValueRef &QCborValueRef::operator=(const QCborValue &other) +{ + d->replaceAt(i, other); + return *this; +} + +QCborValueRef &QCborValueRef::operator=(const QCborValueRef &other) +{ + // ### optimize? + return *this = other.concrete(); +} + +QCborValue QCborValueRef::concrete(QCborValueRef self) Q_DECL_NOTHROW +{ + return self.d->valueAt(self.i); +} + +QCborValue::Type QCborValueRef::concreteType(QCborValueRef self) Q_DECL_NOTHROW +{ + return self.d->elements.at(self.i).type; +} + +inline QCborArray::QCborArray(QCborContainerPrivate &dd) Q_DECL_NOTHROW + : d(&dd) +{ +} + +inline QCborMap::QCborMap(QCborContainerPrivate &dd) Q_DECL_NOTHROW + : d(&dd) +{ +} + +QT_END_NAMESPACE + +#include "qcborarray.cpp" +#include "qcbormap.cpp" + +#include "moc_qcborvalue.cpp" diff --git a/src/corelib/serialization/qcborvalue.h b/src/corelib/serialization/qcborvalue.h new file mode 100644 index 0000000000..d3b82a7e55 --- /dev/null +++ b/src/corelib/serialization/qcborvalue.h @@ -0,0 +1,427 @@ +/**************************************************************************** +** +** 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 QCBORVALUE_H +#define QCBORVALUE_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qcborcommon.h> +#include <QtCore/qregularexpression.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringview.h> +#include <QtCore/qurl.h> +#include <QtCore/quuid.h> +#include <QtCore/qvariant.h> +#include <QtCore/qvector.h> + +#if QT_HAS_INCLUDE(<compare>) +# include <compare> +#endif + +QT_BEGIN_NAMESPACE + +class QCborArray; +class QCborMap; +class QCborStreamReader; +class QCborStreamWriter; + +struct QCborParserError +{ + qint64 offset = 0; + QCborError error = {}; + + QString errorString() const { return error.toString(); } +}; + +class QCborContainerPrivate; +class Q_CORE_EXPORT QCborValue +{ + Q_GADGET +public: + enum EncodingOption { + SortKeysInMaps = 0x01, + UseFloat = 0x02, + UseFloat16 = UseFloat | 0x04, + UseIntegers = 0x08, + + NoTransformation = 0 + }; + Q_DECLARE_FLAGS(EncodingOptions, EncodingOption) + + enum DiagnosticNotationOption { + Compact = 0x00, + LineWrapped = 0x01, + ExtendedFormat = 0x02 + }; + Q_DECLARE_FLAGS(DiagnosticNotationOptions, DiagnosticNotationOption) + + // different from QCborStreamReader::Type because we have more types + enum Type : int { + Integer = 0x00, + ByteArray = 0x40, + String = 0x60, + Array = 0x80, + Map = 0xa0, + Tag = 0xc0, + + // range 0x100 - 0x1ff for Simple Types + SimpleType = 0x100, + False = SimpleType + int(QCborSimpleType::False), + True = SimpleType + int(QCborSimpleType::True), + Null = SimpleType + int(QCborSimpleType::Null), + Undefined = SimpleType + int(QCborSimpleType::Undefined), + + Double = 0x202, + + // extended (tagged) types + DateTime = 0x10000, + Url = 0x10020, + RegularExpression = 0x10023, + Uuid = 0x10025, + + Invalid = -1 + }; + Q_ENUM(Type) + + QCborValue() {} + QCborValue(Type t_) : t(t_) {} + QCborValue(std::nullptr_t) : t(Null) {} + QCborValue(bool b_) : t(b_ ? True : False) {} +#ifndef Q_QDOC + QCborValue(int i) : QCborValue(qint64(i)) {} + QCborValue(unsigned u) : QCborValue(qint64(u)) {} +#endif + QCborValue(qint64 i) : n(i), t(Integer) {} + QCborValue(double v) : t(Double) { memcpy(&n, &v, sizeof(n)); } + QCborValue(QCborSimpleType st) : t(type_helper(st)) {} + + QCborValue(const QByteArray &ba); + QCborValue(const QString &s); + QCborValue(QLatin1String s); +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN QCborValue(const char *s) : QCborValue(QString::fromUtf8(s)) {} +#endif + QCborValue(const QCborArray &a); + QCborValue(const QCborMap &m); + QCborValue(QCborTag tag, const QCborValue &taggedValue = QCborValue()); + QCborValue(QCborKnownTags tag, const QCborValue &taggedValue = QCborValue()) + : QCborValue(QCborTag(tag), taggedValue) + {} + + explicit QCborValue(const QDateTime &dt); + explicit QCborValue(const QUrl &url); + explicit QCborValue(const QRegularExpression &rx); + explicit QCborValue(const QUuid &uuid); + + ~QCborValue() { if (container) dispose(); } + + // make sure const char* doesn't go call the bool constructor + QCborValue(const void *) = delete; + + QCborValue(const QCborValue &other); + QCborValue(QCborValue &&other) Q_DECL_NOTHROW + : n(other.n), container(other.container), t(other.t) + { + other.t = Undefined; + other.container = nullptr; + } + QCborValue &operator=(const QCborValue &other); + QCborValue &operator=(QCborValue &&other) Q_DECL_NOTHROW + { + QCborValue tmp; + qSwap(*this, tmp); + qSwap(other, *this); + return *this; + } + + void swap(QCborValue &other) Q_DECL_NOTHROW + { + qSwap(n, other.n); + qSwap(container, other.container); + qSwap(t, other.t); + } + + Type type() const { return t; } + bool isInteger() const { return type() == Integer; } + 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 isFalse() const { return type() == False; } + bool isTrue() const { return type() == True; } + bool isBool() const { return isFalse() || isTrue(); } + bool isNull() const { return type() == Null; } + bool isUndefined() const { return type() == Undefined; } + bool isDouble() const { return type() == Double; } + bool isDateTime() const { return type() == DateTime; } + bool isUrl() const { return type() == Url; } + bool isRegularExpression() const { return type() == RegularExpression; } + bool isUuid() const { return type() == Uuid; } + bool isInvalid() const { return type() == Invalid; } + bool isContainer() const { return isMap() || isArray(); } + + bool isSimpleType() const + { + return int(type()) >> 8 == int(SimpleType) >> 8; + } + bool isSimpleType(QCborSimpleType st) const + { + return type() == type_helper(st); + } + QCborSimpleType toSimpleType(QCborSimpleType defaultValue = QCborSimpleType::Undefined) const + { + return isSimpleType() ? QCborSimpleType(type() & 0xff) : defaultValue; + } + + qint64 toInteger(qint64 defaultValue = 0) const + { return isInteger() ? value_helper() : isDouble() ? qint64(fp_helper()) : defaultValue; } + bool toBool(bool defaultValue = false) const + { return isBool() ? isTrue() : defaultValue; } + double toDouble(double defaultValue = 0) const + { return isDouble() ? fp_helper() : isInteger() ? double(value_helper()) : defaultValue; } + + QCborTag tag(QCborTag defaultValue = QCborTag(-1)) const; + QCborValue taggedValue(const QCborValue &defaultValue = QCborValue()) const; + QCborValue reinterpretAsTag() const; + + QByteArray toByteArray(const QByteArray &defaultValue = {}) const; + QString toString(const QString &defaultValue = {}) const; + QDateTime toDateTime(const QDateTime &defaultValue = {}) const; + QUrl toUrl(const QUrl &defaultValue = {}) const; + QRegularExpression toRegularExpression(const QRegularExpression &defaultValue = {}) const; + 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; + + int compare(const QCborValue &other) const; +#if QT_HAS_INCLUDE(<compare>) + std::strong_ordering operator<=>(const QCborValue &other) const + { + int c = compare(other); + if (c > 0) return std::partial_ordering::greater; + if (c == 0) return std::partial_ordering::equivalent; + return std::partial_ordering::less; + } +#else + bool operator==(const QCborValue &other) const Q_DECL_NOTHROW + { return compare(other) == 0; } + bool operator!=(const QCborValue &other) const Q_DECL_NOTHROW + { return !(*this == other); } + bool operator<(const QCborValue &other) const + { return compare(other) < 0; } +#endif + + 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); } + QByteArray toCbor(EncodingOptions opt = NoTransformation); + void toCbor(QCborStreamWriter &writer, EncodingOptions opt = NoTransformation); + + QString toDiagnosticNotation(DiagnosticNotationOptions opts = Compact) const; + +private: + friend class QCborValueRef; + friend class QCborContainerPrivate; + qint64 n = 0; + QCborContainerPrivate *container = nullptr; + Type t = Undefined; + + void dispose(); + qint64 value_helper() const + { + return n; + } + + double fp_helper() const + { + Q_STATIC_ASSERT(sizeof(double) == sizeof(n)); + double d; + memcpy(&d, &n, sizeof(d)); + return d; + } + + Q_DECL_CONSTEXPR static Type type_helper(QCborSimpleType st) + { + return Type(quint8(st) | SimpleType); + } +}; +Q_DECLARE_SHARED(QCborValue) + +class Q_CORE_EXPORT QCborValueRef +{ +public: + operator QCborValue() const { return concrete(); } + + QCborValueRef &operator=(const QCborValue &other); + QCborValueRef &operator=(const QCborValueRef &other); + + QCborValue::Type type() const { return concreteType(); } + bool isInteger() const { return type() == QCborValue::Integer; } + bool isByteArray() const { return type() == QCborValue::ByteArray; } + bool isString() const { return type() == QCborValue::String; } + bool isArray() const { return type() == QCborValue::Array; } + bool isMap() const { return type() == QCborValue::Map; } + bool isTag() const { return type() == QCborValue::Tag; } + bool isFalse() const { return type() == QCborValue::False; } + bool isTrue() const { return type() == QCborValue::True; } + bool isBool() const { return isFalse() || isTrue(); } + bool isNull() const { return type() == QCborValue::Null; } + bool isUndefined() const { return type() == QCborValue::Undefined; } + bool isDouble() const { return type() == QCborValue::Double; } + bool isDateTime() const { return type() == QCborValue::DateTime; } + bool isUrl() const { return type() == QCborValue::Url; } + bool isRegularExpression() const { return type() == QCborValue::RegularExpression; } + bool isUuid() const { return type() == QCborValue::Uuid; } + bool isInvalid() const { return type() == QCborValue::Invalid; } + bool isContainer() const { return isMap() || isArray(); } + bool isSimpleType() const + { + return type() >= QCborValue::SimpleType && type() < QCborValue::SimpleType + 0x100; + } + bool isSimpleType(QCborSimpleType st) const + { + return type() == QCborValue::type_helper(st); + } + + QCborTag tag(QCborTag tag = QCborTag(-1)) const + { return concrete().tag(tag); } + QCborValue taggedValue(const QCborValue &defaultValue = QCborValue()) const + { return concrete().taggedValue(defaultValue); } + + qint64 toInteger(qint64 defaultValue = 0) const + { return concrete().toInteger(defaultValue); } + bool toBool(bool defaultValue = false) const + { return concrete().toBool(defaultValue); } + double toDouble(double defaultValue = 0) const + { return concrete().toDouble(defaultValue); } + + QByteArray toByteArray(const QByteArray &defaultValue = {}) const + { return concrete().toByteArray(defaultValue); } + QString toString(const QString &defaultValue = {}) const + { return concrete().toString(defaultValue); } + QDateTime toDateTime(const QDateTime &defaultValue = {}) const + { return concrete().toDateTime(defaultValue); } + QUrl toUrl(const QUrl &defaultValue = {}) const + { return concrete().toUrl(defaultValue); } + QRegularExpression toRegularExpression(const QRegularExpression &defaultValue = {}) const + { return concrete().toRegularExpression(defaultValue); } + 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 + + int compare(const QCborValue &other) const + { return concrete().compare(other); } +#if QT_HAS_INCLUDE(<compare>) + std::strong_ordering operator<=>(const QCborValue &other) const + { + int c = compare(other); + if (c > 0) return std::strong_ordering::greater; + if (c == 0) return std::strong_ordering::equivalent; + return std::strong_ordering::less; + } +#else + bool operator==(const QCborValue &other) const + { return compare(other) == 0; } + bool operator!=(const QCborValue &other) const + { return !(*this == other); } + bool operator<(const QCborValue &other) const + { return compare(other) < 0; } +#endif + + QByteArray toCbor(QCborValue::EncodingOptions opt = QCborValue::NoTransformation) + { return concrete().toCbor(opt); } + void toCbor(QCborStreamWriter &writer, QCborValue::EncodingOptions opt = QCborValue::NoTransformation); + + QString toDiagnosticNotation(QCborValue::DiagnosticNotationOptions opt = QCborValue::Compact) + { return concrete().toDiagnosticNotation(opt); } + +private: + friend class QCborArray; + friend class QCborMap; + friend class QCborContainerPrivate; + friend class QCborValueRefPtr; + + // static so we can pass this by value + static QCborValue concrete(QCborValueRef that) Q_DECL_NOTHROW; + QCborValue concrete() const Q_DECL_NOTHROW { return concrete(*this); } + + static QCborValue::Type concreteType(QCborValueRef self) Q_DECL_NOTHROW Q_DECL_PURE_FUNCTION; + QCborValue::Type concreteType() const Q_DECL_NOTHROW { return concreteType(*this); } + + // this will actually be invalid... + Q_DECL_CONSTEXPR QCborValueRef() : d(nullptr), i(0) {} + + QCborValueRef(QCborContainerPrivate *dd, qsizetype ii) + : d(dd), i(ii) + {} + QCborContainerPrivate *d; + qsizetype i; +}; + +QT_END_NAMESPACE + +#endif // QCBORVALUE_H diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h new file mode 100644 index 0000000000..621fda7158 --- /dev/null +++ b/src/corelib/serialization/qcborvalue_p.h @@ -0,0 +1,366 @@ +/**************************************************************************** +** +** 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 QCBORVALUE_P_H +#define QCBORVALUE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. +// This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qcborvalue.h" + +#include <private/qglobal_p.h> +#include <private/qutfcodec_p.h> + +QT_BEGIN_NAMESPACE + +namespace QtCbor { +struct Undefined {}; +struct Element +{ + enum ValueFlag : quint32 { + IsContainer = 0x0001, + HasByteData = 0x0002, + StringIsUtf16 = 0x0004, + StringIsAscii = 0x0008 + }; + Q_DECLARE_FLAGS(ValueFlags, ValueFlag) + + union { + qint64 value; + QCborContainerPrivate *container; + }; + QCborValue::Type type; + ValueFlags flags = {}; + + Element(qint64 v = 0, QCborValue::Type t = QCborValue::Undefined, ValueFlags f = {}) + : value(v), type(t), flags(f) + {} + + Element(QCborContainerPrivate *d, QCborValue::Type t, ValueFlags f = {}) + : container(d), type(t), flags(f | IsContainer) + {} + + double fpvalue() const + { + double d; + memcpy(&d, &value, sizeof(d)); + return d; + } +}; +Q_DECLARE_OPERATORS_FOR_FLAGS(Element::ValueFlags) + +struct ByteData +{ + QByteArray::size_type len; + + const char *byte() const { return reinterpret_cast<const char *>(this + 1); } + char *byte() { return reinterpret_cast<char *>(this + 1); } + const QChar *utf16() const { return reinterpret_cast<const QChar *>(this + 1); } + QChar *utf16() { return reinterpret_cast<QChar *>(this + 1); } + + QByteArray toByteArray() const { return QByteArray(byte(), len); } + QString toString() const { return QString(utf16(), len / 2); } + QString toUtf8String() const { return QString::fromUtf8(byte(), len); } + + QByteArray asByteArrayView() const { return QByteArray::fromRawData(byte(), len); } + QLatin1String asLatin1() const { return QLatin1String(byte(), len); } + QStringView asStringView() const{ return QStringView(utf16(), len / 2); } + QString asQStringRaw() const { return QString::fromRawData(utf16(), len / 2); } +}; + +Q_STATIC_ASSERT(sizeof(Element) == 16); +Q_STATIC_ASSERT(std::is_pod<ByteData>::value); +} // namespace QtCbor + +Q_DECLARE_TYPEINFO(QtCbor::Element, Q_PRIMITIVE_TYPE); + +class QCborContainerPrivate : public QSharedData +{ + friend class QExplicitlySharedDataPointer<QCborContainerPrivate>; + ~QCborContainerPrivate(); + +public: + QByteArray::size_type usedData = 0; + QByteArray data; + QVector<QtCbor::Element> elements; + + void deref() { if (!ref.deref()) delete this; } + void compact(qsizetype reserved); + static QCborContainerPrivate *clone(QCborContainerPrivate *d, qsizetype reserved = -1); + static QCborContainerPrivate *detach(QCborContainerPrivate *d, qsizetype reserved); + + qptrdiff addByteData(const char *block, qsizetype len) + { + // This function does not do overflow checking, since the len parameter + // is expected to be trusted. There's another version of this function + // in decodeStringFromCbor(), which checks. + + qptrdiff offset = data.size(); + + // align offset + offset += Q_ALIGNOF(QtCbor::ByteData) - 1; + offset &= ~(Q_ALIGNOF(QtCbor::ByteData) - 1); + + qptrdiff increment = qptrdiff(sizeof(QtCbor::ByteData)) + len; + + usedData += increment; + data.resize(offset + increment); + + char *ptr = data.begin() + offset; + auto b = new (ptr) QtCbor::ByteData; + b->len = len; + memcpy(b->byte(), block, len); + + return offset; + } + + const QtCbor::ByteData *byteData(QtCbor::Element e) const + { + if ((e.flags & QtCbor::Element::HasByteData) == 0) + return nullptr; + + size_t offset = size_t(e.value); + Q_ASSERT((offset % Q_ALIGNOF(QtCbor::ByteData)) == 0); + Q_ASSERT(offset + sizeof(QtCbor::ByteData) <= size_t(data.size())); + + auto b = reinterpret_cast<const QtCbor::ByteData *>(data.constData() + offset); + Q_ASSERT(offset + sizeof(*b) + size_t(b->len) <= size_t(data.size())); + return b; + } + const QtCbor::ByteData *byteData(qsizetype idx) const + { + return byteData(elements.at(idx)); + } + + QCborContainerPrivate *containerAt(qsizetype idx, QCborValue::Type type) const + { + const QtCbor::Element &e = elements.at(idx); + if (e.type != type || (e.flags & QtCbor::Element::IsContainer) == 0) + return nullptr; + return e.container; + } + + void replaceAt_complex(QtCbor::Element &e, const QCborValue &value); + void replaceAt_internal(QtCbor::Element &e, const QCborValue &value) + { + if (value.container) + return replaceAt_complex(e, value); + + e.value = value.value_helper(); + e.type = value.type(); + if (value.isContainer()) + e.container = nullptr; + } + void replaceAt(qsizetype idx, const QCborValue &value) + { + QtCbor::Element &e = elements[idx]; + if (e.flags & QtCbor::Element::IsContainer) { + e.container->deref(); + e.container = nullptr; + e.flags = {}; + } else if (e.flags & QtCbor::Element::HasByteData) { + usedData -= byteData(idx)->len + sizeof(QtCbor::ByteData); + } + replaceAt_internal(e, value); + } + void insertAt(qsizetype idx, const QCborValue &value) + { + replaceAt_internal(*elements.insert(elements.begin() + idx, {}), value); + } + + void append(QtCbor::Undefined) + { + elements.append(QtCbor::Element()); + } + void append(qint64 value) + { + elements.append(QtCbor::Element(value , QCborValue::Integer)); + } + void append(QCborTag tag) + { + elements.append(QtCbor::Element(qint64(tag), QCborValue::Tag)); + } + void appendByteData(const char *data, qsizetype len, QCborValue::Type type, + QtCbor::Element::ValueFlags extraFlags = {}) + { + elements.append(QtCbor::Element(addByteData(data, len), type, + QtCbor::Element::HasByteData | extraFlags)); + } + void append(QLatin1String s) + { + if (!QtPrivate::isAscii(s)) + return append(QString(s)); + + // US-ASCII is a subset of UTF-8, so we can keep in 8-bit + appendByteData(s.latin1(), s.size(), QCborValue::String, + QtCbor::Element::StringIsAscii); + } + void append(const QString &s) + { + appendByteData(reinterpret_cast<const char *>(s.constData()), s.size() * 2, + QCborValue::String, QtCbor::Element::StringIsUtf16); + } + void append(const QCborValue &v) + { + insertAt(elements.size(), v); + } + + QByteArray byteArrayAt(qsizetype idx) const + { + const auto &e = elements.at(idx); + const auto data = byteData(e); + if (!data) + return QByteArray(); + return data->toByteArray(); + } + QString stringAt(qsizetype idx) const + { + const auto &e = elements.at(idx); + const auto data = byteData(e); + if (!data) + return QString(); + if (e.flags & QtCbor::Element::StringIsUtf16) + return data->toString(); + if (e.flags & QtCbor::Element::StringIsAscii) + return data->asLatin1(); + return data->toUtf8String(); + } + + static QCborValue makeValue(QCborValue::Type type, qint64 n, QCborContainerPrivate *d = nullptr) + { + QCborValue result(type); + result.n = n; + result.container = d; + if (d) + d->ref.ref(); + return result; + } + + QCborValue valueAt(qsizetype idx) const + { + const auto &e = elements.at(idx); + + if (e.flags & QtCbor::Element::IsContainer) { + if (e.type == QCborValue::Tag && e.container->elements.size() != 2) { + // invalid tags can be created due to incomplete parsing + return makeValue(QCborValue::Invalid, 0, nullptr); + } + return makeValue(e.type, -1, e.container); + } else if (e.flags & QtCbor::Element::HasByteData) { + return makeValue(e.type, idx, const_cast<QCborContainerPrivate *>(this)); + } + return makeValue(e.type, e.value); + } + + static QtCbor::Element elementFromValue(const QCborValue &value) + { + if (value.n >= 0 && value.container) + return value.container->elements.at(value.n); + + QtCbor::Element e; + e.value = value.n; + e.type = value.t; + if (value.container) { + e.container = value.container; + e.flags = QtCbor::Element::IsContainer; + } + return e; + } + + bool stringEqualsElement(qsizetype idx, QLatin1String s) const + { + const auto &e = elements.at(idx); + if (e.type != QCborValue::String) + return false; + + const QtCbor::ByteData *b = byteData(idx); + if (!b) + return s.isEmpty(); + + if (e.flags & QtCbor::Element::StringIsUtf16) + return QtPrivate::compareStrings(b->asStringView(), s) == 0; + return QUtf8::compareUtf8(b->byte(), b->len, s) == 0; + } + bool stringEqualsElement(qsizetype idx, const QString &s) const + { + const auto &e = elements.at(idx); + if (e.type != QCborValue::String) + return false; + + const QtCbor::ByteData *b = byteData(idx); + if (!b) + return s.isEmpty(); + + if (e.flags & QtCbor::Element::StringIsUtf16) + return QtPrivate::compareStrings(b->asStringView(), s) == 0; + return QUtf8::compareUtf8(b->byte(), b->len, s.data(), s.size()) == 0; + } + + static int compareElement_helper(const QCborContainerPrivate *c1, QtCbor::Element e1, + const QCborContainerPrivate *c2, QtCbor::Element e2); + int compareElement(qsizetype idx, const QCborValue &value) const + { + auto &e1 = elements.at(idx); + auto e2 = elementFromValue(value); + return compareElement_helper(this, e1, value.container, e2); + } + + void removeAt(qsizetype idx) + { + replaceAt(idx, {}); + elements.remove(idx); + } + + void decodeValueFromCbor(QCborStreamReader &reader); + void decodeFromCbor(QCborStreamReader &reader); + void decodeStringFromCbor(QCborStreamReader &reader); +}; + +QT_END_NAMESPACE + +#endif // QCBORVALUE_P_H diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri index beb8b96b78..c507a70b10 100644 --- a/src/corelib/serialization/serialization.pri +++ b/src/corelib/serialization/serialization.pri @@ -1,7 +1,11 @@ # Qt data formats core module HEADERS += \ + serialization/qcborarray.h \ serialization/qcborcommon.h \ + serialization/qcbormap.h \ + serialization/qcborvalue.h \ + serialization/qcborvalue_p.h \ serialization/qcborstream.h \ serialization/qdatastream.h \ serialization/qdatastream_p.h \ @@ -20,6 +24,7 @@ HEADERS += \ SOURCES += \ serialization/qcborstream.cpp \ + serialization/qcborvalue.cpp \ serialization/qdatastream.cpp \ serialization/qjson.cpp \ serialization/qjsondocument.cpp \ @@ -32,4 +37,8 @@ SOURCES += \ serialization/qxmlstream.cpp \ serialization/qxmlutils.cpp +false: SOURCES += \ + serialization/qcborarray.cpp \ + serialization/qcbormap.cpp + INCLUDEPATH += ../3rdparty/tinycbor/src |