Revision control
++ // Copyright (C) 2020 Intel Corporation.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:data-parser
++
++ #include "qcborvalue.h"
++ #include "qcborvalue_p.h"
++
++ #include "qcborarray.h"
++ #include "qcbormap.h"
++
++ #include "qjsonarray.h"
++ #include "qjsonobject.h"
++ #include "qjsondocument.h"
++ #include "qjson_p.h"
++
++ #include <qmap.h>
++ #include <qhash.h>
++
++ #include <private/qnumeric_p.h>
++ #include <quuid.h>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace QtCbor;
++
++ enum class ConversionMode { FromRaw, FromVariantToJson };
++
++ static QJsonValue fpToJson(double v)
++ {
++ return qt_is_finite(v) ? QJsonValue(v) : QJsonValue();
++ }
++
++ static QString simpleTypeString(QCborValue::Type t)
++ {
++ int simpleType = t - QCborValue::SimpleType;
++ if (unsigned(simpleType) < 0x100)
++ return QString::fromLatin1("simple(%1)").arg(simpleType);
++
++ // if we got here, we got an unknown type
++ qWarning("QCborValue: found unknown type 0x%x", t);
++ return QString();
++
++ }
++
++ static QString encodeByteArray(const QCborContainerPrivate *d, qsizetype idx, QCborTag encoding)
++ {
++ const ByteData *b = d->byteData(idx);
++ if (!b)
++ return QString();
++
++ QByteArray data = QByteArray::fromRawData(b->byte(), b->len);
++ if (encoding == QCborKnownTags::ExpectedBase16)
++ data = data.toHex();
++ else if (encoding == QCborKnownTags::ExpectedBase64)
++ data = data.toBase64();
++ else
++ data = data.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
++
++ return QString::fromLatin1(data);
++ }
++
++ static QString makeString(const QCborContainerPrivate *d, qsizetype idx,
++ ConversionMode mode = ConversionMode::FromRaw);
++
++ static QString maybeEncodeTag(const QCborContainerPrivate *d)
++ {
++ qint64 tag = d->elements.at(0).value;
++ const Element &e = d->elements.at(1);
++
++ switch (tag) {
++ case qint64(QCborKnownTags::DateTimeString):
++ case qint64(QCborKnownTags::Url):
++ if (e.type == QCborValue::String)
++ return makeString(d, 1);
++ break;
++
++ case qint64(QCborKnownTags::ExpectedBase64url):
++ case qint64(QCborKnownTags::ExpectedBase64):
++ case qint64(QCborKnownTags::ExpectedBase16):
++ if (e.type == QCborValue::ByteArray)
++ return encodeByteArray(d, 1, QCborTag(tag));
++ break;
++
++ case qint64(QCborKnownTags::Uuid):
++ #ifndef QT_BOOTSTRAPPED
++ if (const ByteData *b = d->byteData(e); e.type == QCborValue::ByteArray && b
++ && b->len == sizeof(QUuid))
++ return QUuid::fromRfc4122(b->asByteArrayView()).toString(QUuid::WithoutBraces);
++ #endif
++ break;
++ }
++
++ // don't know what to do, bail out
++ return QString();
++ }
++
++ static QString encodeTag(const QCborContainerPrivate *d)
++ {
++ QString s;
++ if (!d || d->elements.size() != 2)
++ return s; // invalid (incomplete?) tag state
++
++ s = maybeEncodeTag(d);
++ if (s.isNull()) {
++ // conversion failed, ignore the tag and convert the tagged value
++ s = makeString(d, 1);
++ }
++ return s;
++ }
++
++ Q_NEVER_INLINE static QString makeString(const QCborContainerPrivate *d, qsizetype idx,
++ ConversionMode mode)
++ {
++ const auto &e = d->elements.at(idx);
++
++ switch (e.type) {
++ case QCborValue::Integer:
++ return QString::number(qint64(e.value));
++
++ case QCborValue::Double:
++ return QString::number(e.fpvalue());
++
++ case QCborValue::ByteArray:
++ return mode == ConversionMode::FromVariantToJson
++ ? d->stringAt(idx)
++ : encodeByteArray(d, idx, QCborTag(QCborKnownTags::ExpectedBase64url));
++
++ case QCborValue::String:
++ return d->stringAt(idx);
++
++ case QCborValue::Array:
++ case QCborValue::Map:
++ #if defined(QT_BOOTSTRAPPED)
++ Q_UNREACHABLE_RETURN(QString());
++ #else
++ return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact);
++ #endif
++
++ 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::Invalid:
++ return QString();
++
++ case QCborValue::Tag:
++ case QCborValue::DateTime:
++ case QCborValue::Url:
++ case QCborValue::RegularExpression:
++ case QCborValue::Uuid:
++ return encodeTag(e.flags & Element::IsContainer ? e.container : nullptr);
++ }
++
++ // maybe it's a simple type
++ return simpleTypeString(e.type);
++ }
++
++ static QJsonValue convertToJson(QCborContainerPrivate *d, qsizetype idx,
++ ConversionMode mode = ConversionMode::FromRaw);
++
++ static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
++ {
++ qint64 tag = d->elements.at(0).value;
++
++ switch (tag) {
++ case qint64(QCborKnownTags::Url): {
++ #ifdef QT_BOOTSTRAPPED
++ break;
++ #else
++ // use the fully-encoded URL form
++ Element e = d->elements.at(1);
++ const ByteData *bd = d->byteData(e);
++ if (e.type == QCborValue::String) {
++ if (!bd)
++ return QString();
++ return QUrl::fromEncoded(bd->asByteArrayView()).toString(QUrl::FullyEncoded);
++ }
++ Q_FALLTHROUGH();
++ #endif
++ }
++
++ case qint64(QCborKnownTags::DateTimeString):
++ case qint64(QCborKnownTags::ExpectedBase64url):
++ case qint64(QCborKnownTags::ExpectedBase64):
++ case qint64(QCborKnownTags::ExpectedBase16):
++ case qint64(QCborKnownTags::Uuid): {
++ // use the string conversion
++ QString s = maybeEncodeTag(d);
++ if (!s.isNull())
++ return s;
++ }
++ }
++
++ // for all other tags, ignore it and return the converted tagged item
++ return convertToJson(d, 1);
++ }
++
++ // We need to do this because sub-objects may need conversion.
++ static QJsonArray convertToJsonArray(QCborContainerPrivate *d,
++ ConversionMode mode = ConversionMode::FromRaw)
++ {
++ QJsonArray a;
++ if (d) {
++ for (qsizetype idx = 0; idx < d->elements.size(); ++idx)
++ a.append(convertToJson(d, idx, mode));
++ }
++ return a;
++ }
++
++ // We need to do this because the keys need to be sorted and converted to strings
++ // and sub-objects may need recursive conversion.
++ static QJsonObject convertToJsonObject(QCborContainerPrivate *d,
++ ConversionMode mode = ConversionMode::FromRaw)
++ {
++ QJsonObject o;
++ if (d) {
++ for (qsizetype idx = 0; idx < d->elements.size(); idx += 2)
++ o.insert(makeString(d, idx), convertToJson(d, idx + 1, mode));
++ }
++ return o;
++ }
++
++ QJsonValue convertToJson(QCborContainerPrivate *d, qsizetype idx, ConversionMode mode)
++ {
++ // encoding the container itself
++ if (idx == -QCborValue::Array)
++ return convertToJsonArray(d, mode);
++ if (idx == -QCborValue::Map)
++ return convertToJsonObject(d, mode);
++ if (idx < 0) {
++ // tag-like type
++ if (!d || d->elements.size() != 2)
++ return QJsonValue::Undefined; // invalid state
++ return convertExtendedTypeToJson(d);
++ }
++
++ // an element in the container
++ const auto &e = d->elements.at(idx);
++ switch (e.type) {
++ case QCborValue::Integer:
++ return QJsonValue(e.value);
++ case QCborValue::ByteArray:
++ if (mode == ConversionMode::FromVariantToJson) {
++ const auto value = makeString(d, idx, mode);
++ return value.isEmpty() ? QJsonValue() : QJsonPrivate::Value::fromTrustedCbor(value);
++ }
++ break;
++ case QCborValue::RegularExpression:
++ if (mode == ConversionMode::FromVariantToJson)
++ return QJsonValue();
++ break;
++ case QCborValue::String:
++ case QCborValue::SimpleType:
++ // make string
++ break;
++
++ case QCborValue::Array:
++ case QCborValue::Map:
++ case QCborValue::Tag:
++ case QCborValue::DateTime:
++ case QCborValue::Url:
++ case QCborValue::Uuid:
++ // recurse
++ return convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type,
++ mode);
++
++ case QCborValue::Null:
++ case QCborValue::Undefined:
++ case QCborValue::Invalid:
++ return QJsonValue();
++
++ case QCborValue::False:
++ return false;
++
++ case QCborValue::True:
++ return true;
++
++ case QCborValue::Double:
++ return fpToJson(e.fpvalue());
++ }
++
++ return QJsonPrivate::Value::fromTrustedCbor(makeString(d, idx, mode));
++ }
++
++ /*!
++ Converts this QCborValue object to an equivalent representation in JSON and
++ returns it as a QJsonValue.
++
++ Please note that CBOR contains a richer and wider type set than JSON, so
++ some information may be lost in this conversion. The following table
++ compares CBOR types to JSON types and indicates whether information may be
++ lost or not.
++
++ \table
++ \header \li CBOR Type \li JSON Type \li Comments
++ \row \li Bool \li Bool \li No data loss possible
++ \row \li Double \li Number \li Infinities and NaN will be converted to Null;
++ no data loss for other values
++ \row \li Integer \li Number \li Data loss possible in the conversion if the
++ integer is larger than 2\sup{53} or smaller
++ than -2\sup{53}.
++ \row \li Null \li Null \li No data loss possible
++ \row \li Undefined \li Null \li Type information lost
++ \row \li String \li String \li No data loss possible
++ \row \li Byte Array \li String \li Converted to a lossless encoding like Base64url,
++ but the distinction between strings and byte
++ arrays is lost
++ \row \li Other simple types \li String \li Type information lost
++ \row \li Array \li Array \li Conversion applies to each contained value
++ \row \li Map \li Object \li Keys are converted to string; values converted
++ according to this table
++ \row \li Tags and extended types \li Special \li The tag number itself is lost and the tagged
++ value is converted to JSON
++ \endtable
++
++ For information on the conversion of CBOR map keys to string, see
++ QCborMap::toJsonObject().
++
++ If this QCborValue contains the undefined value, this function will return
++ an undefined QJsonValue too. Note that JSON does not support undefined
++ values and undefined QJsonValues are an extension to the specification.
++ They cannot be held in a QJsonArray or QJsonObject, but can be returned
++ from functions to indicate a failure. For all other intents and purposes,
++ they are the same as null.
++
++ \section3 Special handling of tags and extended types
++
++ Some tags are handled specially and change the transformation of the tagged
++ value from CBOR to JSON. The following table lists those special cases:
++
++ \table
++ \header \li Tag \li CBOR type \li Transformation
++ \row \li ExpectedBase64url \li Byte array \li Encodes the byte array as Base64url
++ \row \li ExpectedBase64 \li Byte array \li Encodes the byte array as Base64
++ \row \li ExpectedBase16 \li Byte array \li Encodes the byte array as hex
++ \row \li Url \li Url and String \li Uses QUrl::toEncoded() to normalize the
++ encoding to the URL's fully encoded format
++ \row \li Uuid \li Uuid and Byte array \li Uses QUuid::toString() to create
++ the string representation
++ \endtable
++
++ \sa fromJsonValue(), toVariant(), QCborArray::toJsonArray(), QCborMap::toJsonObject()
++ */
++ QJsonValue QCborValue::toJsonValue() const
++ {
++ if (container)
++ return convertToJson(container, n < 0 ? -type() : n);
++
++ // simple values
++ switch (type()) {
++ case False:
++ return false;
++
++ case Integer:
++ return QJsonPrivate::Value::fromTrustedCbor(*this);
++
++ case True:
++ return true;
++
++ case Null:
++ case Undefined:
++ case Invalid:
++ return QJsonValue();
++
++ case Double:
++ return fpToJson(fp_helper());
++
++ case SimpleType:
++ break;
++
++ case ByteArray:
++ case String:
++ // empty strings
++ return QJsonValue::String;
++
++ case Array:
++ // empty array
++ return QJsonArray();
++
++ case Map:
++ // empty map
++ return QJsonObject();
++
++ case Tag:
++ case DateTime:
++ case Url:
++ case RegularExpression:
++ case Uuid:
++ // Reachable, but invalid in Json
++ return QJsonValue::Undefined;
++ }
++
++ return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type()));
++ }
++
++ #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
++ QJsonValue QCborValueRef::toJsonValue() const
++ {
++ return convertToJson(d, i);
++ }
++ #endif
++
++ /*!
++ Recursively converts every \l QCborValue element in this array to JSON
++ using QCborValue::toJsonValue() and returns the corresponding QJsonArray
++ composed of those elements.
++
++ Please note that CBOR contains a richer and wider type set than JSON, so
++ some information may be lost in this conversion. For more details on what
++ conversions are applied, see QCborValue::toJsonValue().
++
++ \sa fromJsonArray(), QCborValue::toJsonValue(), QCborMap::toJsonObject(), toVariantList()
++ */
++ QJsonArray QCborArray::toJsonArray() const
++ {
++ return convertToJsonArray(d.data());
++ }
++
++ #ifndef QT_NO_VARIANT
++ QJsonArray QJsonPrivate::Variant::toJsonArray(const QVariantList &list)
++ {
++ const auto cborArray = QCborArray::fromVariantList(list);
++ return convertToJsonArray(cborArray.d.data(), ConversionMode::FromVariantToJson);
++ }
++ #endif // !QT_NO_VARIANT
++
++ /*!
++ Recursively converts every \l QCborValue value in this map to JSON using
++ QCborValue::toJsonValue() and creates a string key for all keys that aren't
++ strings, then returns the corresponding QJsonObject composed of those
++ associations.
++
++ Please note that CBOR contains a richer and wider type set than JSON, so
++ some information may be lost in this conversion. For more details on what
++ conversions are applied, see QCborValue::toJsonValue().
++
++ \section3 Map key conversion to string
++
++ JSON objects are defined as having string keys, unlike CBOR, so the
++ conversion of a QCborMap to QJsonObject will imply a step of
++ "stringification" of the key values. The conversion will use the special
++ handling of tags and extended types from above and will also convert the
++ rest of the types as follows:
++
++ \table
++ \header \li Type \li Transformation
++ \row \li Bool \li "true" and "false"
++ \row \li Null \li "null"
++ \row \li Undefined \li "undefined"
++ \row \li Integer \li The decimal string form of the number
++ \row \li Double \li The decimal string form of the number
++ \row \li Byte array \li Unless tagged differently (see above), encoded as
++ Base64url
++ \row \li Array \li Replaced by the compact form of its
++ \l{QCborValue::toDiagnosticNotation()}{Diagnostic notation}
++ \row \li Map \li Replaced by the compact form of its
++ \l{QCborValue::toDiagnosticNotation()}{Diagnostic notation}
++ \row \li Tags and extended types \li Tag number is dropped and the tagged value is converted
++ to string
++ \endtable
++
++ \sa fromJsonObject(), QCborValue::toJsonValue(), QCborArray::toJsonArray(), toVariantMap()
++ */
++ QJsonObject QCborMap::toJsonObject() const
++ {
++ return convertToJsonObject(d.data());
++ }
++
++ #ifndef QT_NO_VARIANT
++ QJsonObject QJsonPrivate::Variant::toJsonObject(const QVariantMap &map)
++ {
++ const auto cborMap = QCborMap::fromVariantMap(map);
++ return convertToJsonObject(cborMap.d.data(), ConversionMode::FromVariantToJson);
++ }
++
++ /*!
++ Converts this value to a native Qt type and returns the corresponding QVariant.
++
++ The following table lists the mapping performed between \l{Type}{QCborValue
++ types} and \l{QMetaType::Type}{Qt meta types}.
++
++ \table
++ \header \li CBOR Type \li Qt or C++ type \li Notes
++ \row \li Integer \li \l qint64 \li
++ \row \li Double \li \c double \li
++ \row \li Bool \li \c bool \li
++ \row \li Null \li \c std::nullptr_t \li
++ \row \li Undefined \li no type (QVariant()) \li
++ \row \li Byte array \li \l QByteArray \li
++ \row \li String \li \l QString \li
++ \row \li Array \li \l QVariantList \li Recursively converts all values
++ \row \li Map \li \l QVariantMap \li Key types are "stringified"
++ \row \li Other simple types \li \l QCborSimpleType \li
++ \row \li DateTime \li \l QDateTime \li
++ \row \li Url \li \l QUrl \li
++ \row \li RegularExpression \li \l QRegularExpression \li
++ \row \li Uuid \li \l QUuid \li
++ \row \li Other tags \li Special \li The tag is ignored and the tagged
++ value is converted using this
++ function
++ \endtable
++
++ Note that values in both CBOR Maps and Arrays are converted recursively
++ using this function too and placed in QVariantMap and QVariantList instead.
++ You will not find QCborMap and QCborArray stored inside the QVariants.
++
++ QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
++ to QVariantMap will imply a step of "stringification" of the key values.
++ See QCborMap::toJsonObject() for details.
++
++ \sa fromVariant(), toJsonValue(), QCborArray::toVariantList(), QCborMap::toVariantMap()
++ */
++ QVariant QCborValue::toVariant() const
++ {
++ switch (type()) {
++ case Integer:
++ return toInteger();
++
++ case Double:
++ return toDouble();
++
++ case SimpleType:
++ break;
++
++ case False:
++ case True:
++ return isTrue();
++
++ case Null:
++ return QVariant::fromValue(nullptr);
++
++ case Undefined:
++ return QVariant();
++
++ case ByteArray:
++ return toByteArray();
++
++ case String:
++ return toString();
++
++ case Array:
++ return toArray().toVariantList();
++
++ case Map:
++ return toMap().toVariantMap();
++
++ case Tag:
++ // ignore tags
++ return taggedValue().toVariant();
++
++ #if QT_CONFIG(datestring)
++ case DateTime:
++ return toDateTime();
++ #endif
++
++ #ifndef QT_BOOTSTRAPPED
++ case Url:
++ return toUrl();
++
++ # if QT_CONFIG(regularexpression)
++ case RegularExpression:
++ return toRegularExpression();
++ # endif
++
++ case Uuid:
++ return toUuid();
++ #endif
++
++ case Invalid:
++ return QVariant();
++
++ default:
++ break;
++ }
++
++ if (isSimpleType())
++ return QVariant::fromValue(toSimpleType());
++
++ Q_UNREACHABLE_RETURN(QVariant());
++ }
++ #endif // !QT_NO_VARIANT
++
++ /*!
++ Converts the JSON value contained in \a v into its corresponding CBOR value
++ and returns it. There is no data loss in converting from JSON to CBOR, as
++ the CBOR type set is richer than JSON's. Additionally, values converted to
++ CBOR using this function can be converted back to JSON using toJsonValue()
++ with no data loss.
++
++ The following table lists the mapping of JSON types to CBOR types:
++
++ \table
++ \header \li JSON Type \li CBOR Type
++ \row \li Bool \li Bool
++ \row \li Number \li Integer (if the number has no fraction and is in the \l qint64
++ range) or Double
++ \row \li String \li String
++ \row \li Array \li Array
++ \row \li Object \li Map
++ \row \li Null \li Null
++ \endtable
++
++ \l QJsonValue can also be undefined, indicating a previous operation that
++ failed to complete (for example, searching for a key not present in an
++ object). Undefined values are not JSON types and may not appear in JSON
++ arrays and objects, but this function does return the QCborValue undefined
++ value if the corresponding QJsonValue is undefined.
++
++ \sa toJsonValue(), fromVariant(), QCborArray::fromJsonArray(), QCborMap::fromJsonObject()
++ */
++ QCborValue QCborValue::fromJsonValue(const QJsonValue &v)
++ {
++ switch (v.type()) {
++ case QJsonValue::Bool:
++ return v.toBool();
++ case QJsonValue::Double: {
++ if (v.value.t == Integer)
++ return v.toInteger();
++ return v.toDouble();
++ }
++ case QJsonValue::String:
++ return v.toString();
++ case QJsonValue::Array:
++ return QCborArray::fromJsonArray(v.toArray());
++ case QJsonValue::Object:
++ return QCborMap::fromJsonObject(v.toObject());
++ case QJsonValue::Null:
++ return nullptr;
++ case QJsonValue::Undefined:
++ break;
++ }
++ return QCborValue();
++ }
++
++ #ifndef QT_NO_VARIANT
++ static void appendVariant(QCborContainerPrivate *d, const QVariant &variant)
++ {
++ // Handle strings and byte arrays directly, to avoid creating a temporary
++ // dummy container to hold their data.
++ int type = variant.metaType().id();
++ if (type == QMetaType::QString) {
++ d->append(variant.toString());
++ } else if (type == QMetaType::QByteArray) {
++ QByteArray ba = variant.toByteArray();
++ d->appendByteData(ba.constData(), ba.size(), QCborValue::ByteArray);
++ } else {
++ // For everything else, use the function below.
++ d->append(QCborValue::fromVariant(variant));
++ }
++ }
++
++ /*!
++ Converts the QVariant \a variant into QCborValue and returns it.
++
++ QVariants may contain a large list of different meta types, many of which
++ have no corresponding representation in CBOR. That includes all
++ user-defined meta types. When preparing transmission using CBOR, it is
++ suggested to encode carefully each value to prevent loss of representation.
++
++ The following table lists the conversion this function will apply:
++
++ \table
++ \header \li Qt (C++) type \li CBOR type
++ \row \li invalid (QVariant()) \li Undefined
++ \row \li \c bool \li Bool
++ \row \li \c std::nullptr_t \li Null
++ \row \li \c short, \c ushort, \c int, \c uint, \l qint64 \li Integer
++ \row \li \l quint64 \li Integer, or Double if outside the range of qint64
++ \row \li \c float, \c double \li Double
++ \row \li \l QByteArray \li ByteArray
++ \row \li \l QDateTime \li DateTime
++ \row \li \l QCborSimpleType \li Simple type
++ \row \li \l QJsonArray \li Array, converted using QCborArray::formJsonArray()
++ \row \li \l QJsonDocument \li Array or Map
++ \row \li \l QJsonObject \li Map, converted using QCborMap::fromJsonObject()
++ \row \li \l QJsonValue \li converted using fromJsonValue()
++ \row \li \l QRegularExpression \li RegularExpression
++ \row \li \l QString \li String
++ \row \li \l QStringList \li Array
++ \row \li \l QVariantHash \li Map
++ \row \li \l QVariantList \li Array
++ \row \li \l QVariantMap \li Map
++ \row \li \l QUrl \li Url
++ \row \li \l QUuid \li Uuid
++ \endtable
++
++ If QVariant::isNull() returns true, a null QCborValue is returned or
++ inserted into the list or object, regardless of the type carried by
++ QVariant. Note the behavior change in Qt 6.0 affecting QVariant::isNull()
++ also affects this function.
++
++ For other types not listed above, a conversion to string will be attempted,
++ usually but not always by calling QVariant::toString(). If the conversion
++ fails the value is replaced by an Undefined CBOR value. Note that
++ QVariant::toString() is also lossy for the majority of types.
++
++ Please note that the conversions via QVariant::toString() are subject to
++ change at any time. Both QVariant and QCborValue may be extended in the
++ future to support more types, which will result in a change in how this
++ function performs conversions.
++
++ \sa toVariant(), fromJsonValue(), QCborArray::toVariantList(), QCborMap::toVariantMap(), QJsonValue::fromVariant()
++ */
++ QCborValue QCborValue::fromVariant(const QVariant &variant)
++ {
++ switch (variant.metaType().id()) {
++ case QMetaType::UnknownType:
++ return {};
++ case QMetaType::Nullptr:
++ return nullptr;
++ case QMetaType::Bool:
++ return variant.toBool();
++ case QMetaType::Short:
++ case QMetaType::UShort:
++ case QMetaType::Int:
++ case QMetaType::LongLong:
++ case QMetaType::Long:
++ case QMetaType::UInt:
++ return variant.toLongLong();
++ case QMetaType::ULong:
++ case QMetaType::ULongLong:
++ if (variant.toULongLong() <= static_cast<uint64_t>(std::numeric_limits<qint64>::max()))
++ return variant.toLongLong();
++ Q_FALLTHROUGH();
++ case QMetaType::Float16:
++ case QMetaType::Float:
++ case QMetaType::Double:
++ return variant.toDouble();
++ case QMetaType::QString:
++ return variant.toString();
++ case QMetaType::QStringList:
++ return QCborArray::fromStringList(variant.toStringList());
++ case QMetaType::QByteArray:
++ return variant.toByteArray();
++ #if QT_CONFIG(datestring)
++ case QMetaType::QDateTime:
++ return QCborValue(variant.toDateTime());
++ #endif
++ #ifndef QT_BOOTSTRAPPED
++ case QMetaType::QUrl:
++ return QCborValue(variant.toUrl());
++ case QMetaType::QUuid:
++ return QCborValue(variant.toUuid());
++ #endif
++ case QMetaType::QVariantList:
++ return QCborArray::fromVariantList(variant.toList());
++ case QMetaType::QVariantMap:
++ return QCborMap::fromVariantMap(variant.toMap());
++ case QMetaType::QVariantHash:
++ return QCborMap::fromVariantHash(variant.toHash());
++ #ifndef QT_BOOTSTRAPPED
++ #if QT_CONFIG(regularexpression)
++ case QMetaType::QRegularExpression:
++ return QCborValue(variant.toRegularExpression());
++ #endif
++ case QMetaType::QJsonValue:
++ return fromJsonValue(variant.toJsonValue());
++ case QMetaType::QJsonObject:
++ return QCborMap::fromJsonObject(variant.toJsonObject());
++ case QMetaType::QJsonArray:
++ return QCborArray::fromJsonArray(variant.toJsonArray());
++ case QMetaType::QJsonDocument: {
++ QJsonDocument doc = variant.toJsonDocument();
++ if (doc.isArray())
++ return QCborArray::fromJsonArray(doc.array());
++ return QCborMap::fromJsonObject(doc.object());
++ }
++ case QMetaType::QCborValue:
++ return qvariant_cast<QCborValue>(variant);
++ case QMetaType::QCborArray:
++ return qvariant_cast<QCborArray>(variant);
++ case QMetaType::QCborMap:
++ return qvariant_cast<QCborMap>(variant);
++ case QMetaType::QCborSimpleType:
++ return qvariant_cast<QCborSimpleType>(variant);
++ #endif
++ default:
++ break;
++ }
++
++ if (variant.isNull())
++ return QCborValue(nullptr);
++
++ QString string = variant.toString();
++ if (string.isNull())
++ return QCborValue(); // undefined
++ return string;
++ }
++
++ /*!
++ Recursively converts each \l QCborValue in this array using
++ QCborValue::toVariant() and returns the QVariantList composed of the
++ converted items.
++
++ Conversion to \l QVariant is not completely lossless. Please see the
++ documentation in QCborValue::toVariant() for more information.
++
++ \sa fromVariantList(), fromStringList(), toJsonArray(),
++ QCborValue::toVariant(), QCborMap::toVariantMap()
++ */
++ QVariantList QCborArray::toVariantList() const
++ {
++ QVariantList retval;
++ retval.reserve(size());
++ for (qsizetype i = 0; i < size(); ++i)
++ retval.append(d->valueAt(i).toVariant());
++ return retval;
++ }
++
++ /*!
++ Returns a QCborArray containing all the strings found in the \a list list.
++
++ \sa fromVariantList(), fromJsonArray()
++ */
++ QCborArray QCborArray::fromStringList(const QStringList &list)
++ {
++ QCborArray a;
++ a.detach(list.size());
++ for (const QString &s : list)
++ a.d->append(s);
++ return a;
++ }
++
++ /*!
++ Converts all the items in the \a list to CBOR using
++ QCborValue::fromVariant() and returns the array composed of those elements.
++
++ Conversion from \l QVariant is not completely lossless. Please see the
++ documentation in QCborValue::fromVariant() for more information.
++
++ \sa toVariantList(), fromStringList(), fromJsonArray(), QCborMap::fromVariantMap()
++ */
++ QCborArray QCborArray::fromVariantList(const QVariantList &list)
++ {
++ QCborArray a;
++ a.detach(list.size());
++ for (const QVariant &v : list)
++ appendVariant(a.d.data(), v);
++ return a;
++ }
++ #endif // !QT_NO_VARIANT
++
++ /*!
++ Converts all JSON items found in the \a array array to CBOR using
++ QCborValue::fromJson(), and returns the CBOR array composed of those
++ elements.
++
++ This conversion is lossless, as the CBOR type system is a superset of
++ JSON's. Moreover, the array returned by this function can be converted back
++ to the original \a array by using toJsonArray().
++
++ \sa toJsonArray(), toVariantList(), QCborValue::fromJsonValue(), QCborMap::fromJsonObject()
++ */
++ QCborArray QCborArray::fromJsonArray(const QJsonArray &array)
++ {
++ QCborArray result;
++ result.d = array.a;
++ return result;
++ }
++
++ /*!
++ \overload
++ \since 6.3
++ */
++ QCborArray QCborArray::fromJsonArray(QJsonArray &&array) noexcept
++ {
++ QCborArray result;
++ result.d = std::exchange(array.a, {});
++ return result;
++
++ }
++
++ #ifndef QT_NO_VARIANT
++ /*!
++ Converts the CBOR values to QVariant using QCborValue::toVariant() and
++ "stringifies" all the CBOR keys in this map, returning the QVariantMap that
++ results from that association list.
++
++ QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
++ to QVariantMap will imply a step of "stringification" of the key values.
++ See QCborMap::toJsonObject() for details.
++
++ In addition, the conversion to \l QVariant is not completely lossless.
++ Please see the documentation in QCborValue::toVariant() for more
++ information.
++
++ \sa fromVariantMap(), toVariantHash(), toJsonObject(), QCborValue::toVariant(),
++ QCborArray::toVariantList()
++ */
++ QVariantMap QCborMap::toVariantMap() const
++ {
++ QVariantMap retval;
++ for (qsizetype i = 0; i < 2 * size(); i += 2)
++ retval.insert(makeString(d.data(), i), d->valueAt(i + 1).toVariant());
++ return retval;
++ }
++
++ /*!
++ Converts the CBOR values to QVariant using QCborValue::toVariant() and
++ "stringifies" all the CBOR keys in this map, returning the QVariantHash that
++ results from that association list.
++
++ QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
++ to QVariantMap will imply a step of "stringification" of the key values.
++ See QCborMap::toJsonObject() for details.
++
++ In addition, the conversion to \l QVariant is not completely lossless.
++ Please see the documentation in QCborValue::toVariant() for more
++ information.
++
++ \sa fromVariantHash(), toVariantMap(), toJsonObject(), QCborValue::toVariant(),
++ QCborArray::toVariantList()
++ */
++ QVariantHash QCborMap::toVariantHash() const
++ {
++ QVariantHash retval;
++ retval.reserve(size());
++ for (qsizetype i = 0; i < 2 * size(); i += 2)
++ retval.insert(makeString(d.data(), i), d->valueAt(i + 1).toVariant());
++ return retval;
++ }
++
++ /*!
++ Converts all the items in \a map to CBOR using QCborValue::fromVariant()
++ and returns the map composed of those elements.
++
++ Conversion from \l QVariant is not completely lossless. Please see the
++ documentation in QCborValue::fromVariant() for more information.
++
++ \sa toVariantMap(), fromVariantHash(), fromJsonObject(), QCborValue::fromVariant()
++ */
++ QCborMap QCborMap::fromVariantMap(const QVariantMap &map)
++ {
++ QCborMap m;
++ m.detach(map.size());
++ QCborContainerPrivate *d = m.d.data();
++
++ auto it = map.begin();
++ auto end = map.end();
++ for ( ; it != end; ++it) {
++ d->append(it.key());
++ appendVariant(d, it.value());
++ }
++ return m;
++ }
++
++ /*!
++ Converts all the items in \a hash to CBOR using QCborValue::fromVariant()
++ and returns the map composed of those elements.
++
++ Conversion from \l QVariant is not completely lossless. Please see the
++ documentation in QCborValue::fromVariant() for more information.
++
++ \sa toVariantHash(), fromVariantMap(), fromJsonObject(), QCborValue::fromVariant()
++ */
++ QCborMap QCborMap::fromVariantHash(const QVariantHash &hash)
++ {
++ QCborMap m;
++ m.detach(hash.size());
++ QCborContainerPrivate *d = m.d.data();
++
++ auto it = hash.begin();
++ auto end = hash.end();
++ for ( ; it != end; ++it) {
++ d->append(it.key());
++ appendVariant(d, it.value());
++ }
++ return m;
++ }
++ #endif // !QT_NO_VARIANT
++
++ /*!
++ Converts all JSON items found in the \a obj object to CBOR using
++ QCborValue::fromJson(), and returns the map composed of those elements.
++
++ This conversion is lossless, as the CBOR type system is a superset of
++ JSON's. Moreover, the map returned by this function can be converted back
++ to the original \a obj by using toJsonObject().
++
++ \sa toJsonObject(), toVariantMap(), QCborValue::fromJsonValue(), QCborArray::fromJsonArray()
++ */
++ QCborMap QCborMap::fromJsonObject(const QJsonObject &obj)
++ {
++ QCborMap result;
++ result.d = obj.o;
++ return result;
++ }
++
++ /*!
++ \overload
++ \since 6.3
++ */
++ QCborMap QCborMap::fromJsonObject(QJsonObject &&obj) noexcept
++ {
++ QCborMap result;
++ result.d = std::exchange(obj.o, {});
++ return result;
++ }
++
++ QT_END_NAMESPACE