| /**************************************************************************** |
| ** |
| ** 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> |
| |
| #include <math.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) |
| Q_STATIC_ASSERT(sizeof(Element) == 16); |
| |
| 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(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: |
| enum ContainerDisposition { CopyContainer, MoveContainer }; |
| |
| 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); |
| static QCborContainerPrivate *grow(QCborContainerPrivate *d, qsizetype index); |
| |
| 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; |
| if (block) |
| 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, ContainerDisposition disp); |
| void replaceAt_internal(QtCbor::Element &e, const QCborValue &value, ContainerDisposition disp) |
| { |
| if (value.container) |
| return replaceAt_complex(e, value, disp); |
| |
| e = { value.value_helper(), value.type() }; |
| if (value.isContainer()) |
| e.container = nullptr; |
| } |
| void replaceAt(qsizetype idx, const QCborValue &value, ContainerDisposition disp = CopyContainer) |
| { |
| QtCbor::Element &e = elements[idx]; |
| if (e.flags & QtCbor::Element::IsContainer) { |
| e.container->deref(); |
| e.container = nullptr; |
| e.flags = {}; |
| } else if (auto b = byteData(e)) { |
| usedData -= b->len + sizeof(QtCbor::ByteData); |
| } |
| replaceAt_internal(e, value, disp); |
| } |
| void insertAt(qsizetype idx, const QCborValue &value, ContainerDisposition disp = CopyContainer) |
| { |
| replaceAt_internal(*elements.insert(elements.begin() + idx, {}), value, disp); |
| } |
| |
| 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 appendAsciiString(QStringView s); |
| |
| #if QT_STRINGVIEW_LEVEL < 2 |
| void append(const QString &s) |
| { |
| append(qToStringViewIgnoringNull(s)); |
| } |
| #endif |
| |
| void append(QStringView s) |
| { |
| if (QtPrivate::isAscii(s)) |
| appendAsciiString(s); |
| else |
| appendByteData(reinterpret_cast<const char *>(s.utf16()), 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 void resetValue(QCborValue &v) |
| { |
| v.container = nullptr; |
| } |
| |
| static QCborValue makeValue(QCborValue::Type type, qint64 n, QCborContainerPrivate *d = nullptr, |
| ContainerDisposition disp = CopyContainer) |
| { |
| QCborValue result(type); |
| result.n = n; |
| result.container = d; |
| if (d && disp == CopyContainer) |
| 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); |
| } |
| QCborValue extractAt_complex(QtCbor::Element e); |
| QCborValue extractAt(qsizetype idx) |
| { |
| QtCbor::Element e; |
| qSwap(e, elements[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 |
| e.container->deref(); |
| return makeValue(QCborValue::Invalid, 0, nullptr); |
| } |
| return makeValue(e.type, -1, e.container, MoveContainer); |
| } else if (e.flags & QtCbor::Element::HasByteData) { |
| return extractAt_complex(e); |
| } |
| 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; |
| } |
| |
| static int compareUtf8(const QtCbor::ByteData *b, const QLatin1String &s) |
| { |
| return QUtf8::compareUtf8(b->byte(), b->len, s); |
| } |
| |
| static int compareUtf8(const QtCbor::ByteData *b, QStringView s) |
| { |
| return QUtf8::compareUtf8(b->byte(), b->len, s.data(), s.size()); |
| } |
| |
| template<typename String> |
| int stringCompareElement(const QtCbor::Element &e, String s) const |
| { |
| if (e.type != QCborValue::String) |
| return int(e.type) - int(QCborValue::String); |
| |
| const QtCbor::ByteData *b = byteData(e); |
| if (!b) |
| return s.isEmpty() ? 0 : -1; |
| |
| if (e.flags & QtCbor::Element::StringIsUtf16) |
| return QtPrivate::compareStrings(b->asStringView(), s); |
| return compareUtf8(b, s); |
| } |
| |
| template<typename String> |
| bool stringEqualsElement(const QtCbor::Element &e, String s) const |
| { |
| return stringCompareElement(e, s) == 0; |
| } |
| |
| template<typename String> |
| bool stringEqualsElement(qsizetype idx, String s) const |
| { |
| return stringEqualsElement(elements.at(idx), s); |
| } |
| |
| 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 |