1 /****************************************************************************
2 **
3 ** Copyright (C) 2019 Intel Corporation.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qcborvalue.h"
41 #include "qcborvalue_p.h"
42 
43 #include "qcborarray.h"
44 #include "qcbormap.h"
45 
46 #include "qjsonarray.h"
47 #include "qjsonobject.h"
48 #include "qjsondocument.h"
49 #include "qjson_p.h"
50 
51 #include <private/qnumeric_p.h>
52 #include <quuid.h>
53 
54 QT_BEGIN_NAMESPACE
55 
56 using namespace QtCbor;
57 using ConversionMode = QCborContainerPrivate::ConversionMode;
58 
fpToJson(double v)59 static QJsonValue fpToJson(double v)
60 {
61     return qt_is_finite(v) ? QJsonValue(v) : QJsonValue();
62 }
63 
simpleTypeString(QCborValue::Type t)64 static QString simpleTypeString(QCborValue::Type t)
65 {
66     int simpleType = t - QCborValue::SimpleType;
67     if (unsigned(simpleType) < 0x100)
68         return QString::fromLatin1("simple(%1)").arg(simpleType);
69 
70     // if we got here, we got an unknown type
71     qWarning("QCborValue: found unknown type 0x%x", t);
72     return QString();
73 
74 }
75 
encodeByteArray(const QCborContainerPrivate * d,qsizetype idx,QCborTag encoding)76 static QString encodeByteArray(const QCborContainerPrivate *d, qsizetype idx, QCborTag encoding)
77 {
78     const ByteData *b = d->byteData(idx);
79     if (!b)
80         return QString();
81 
82     QByteArray data = QByteArray::fromRawData(b->byte(), b->len);
83     if (encoding == QCborKnownTags::ExpectedBase16)
84         data = data.toHex();
85     else if (encoding == QCborKnownTags::ExpectedBase64)
86         data = data.toBase64();
87     else
88         data = data.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
89 
90     return QString::fromLatin1(data, data.size());
91 }
92 
93 static QString makeString(const QCborContainerPrivate *d, qsizetype idx,
94                           ConversionMode mode = ConversionMode::FromRaw);
95 
maybeEncodeTag(const QCborContainerPrivate * d)96 static QString maybeEncodeTag(const QCborContainerPrivate *d)
97 {
98     qint64 tag = d->elements.at(0).value;
99     const Element &e = d->elements.at(1);
100     const ByteData *b = d->byteData(e);
101 
102     switch (tag) {
103     case qint64(QCborKnownTags::DateTimeString):
104     case qint64(QCborKnownTags::Url):
105         if (e.type == QCborValue::String)
106             return makeString(d, 1);
107         break;
108 
109     case qint64(QCborKnownTags::ExpectedBase64url):
110     case qint64(QCborKnownTags::ExpectedBase64):
111     case qint64(QCborKnownTags::ExpectedBase16):
112         if (e.type == QCborValue::ByteArray)
113             return encodeByteArray(d, 1, QCborTag(tag));
114         break;
115 
116     case qint64(QCborKnownTags::Uuid):
117         if (e.type == QCborValue::ByteArray && b->len == sizeof(QUuid))
118             return QUuid::fromRfc4122(b->asByteArrayView()).toString(QUuid::WithoutBraces);
119     }
120 
121     // don't know what to do, bail out
122     return QString();
123 }
124 
encodeTag(const QCborContainerPrivate * d)125 static QString encodeTag(const QCborContainerPrivate *d)
126 {
127     QString s;
128     if (!d || d->elements.size() != 2)
129         return s;               // invalid (incomplete?) tag state
130 
131     s = maybeEncodeTag(d);
132     if (s.isNull()) {
133         // conversion failed, ignore the tag and convert the tagged value
134         s = makeString(d, 1);
135     }
136     return s;
137 }
138 
makeString(const QCborContainerPrivate * d,qsizetype idx,ConversionMode mode)139 static Q_NEVER_INLINE QString makeString(const QCborContainerPrivate *d, qsizetype idx,
140                                          ConversionMode mode)
141 {
142     const auto &e = d->elements.at(idx);
143 
144     switch (e.type) {
145     case QCborValue::Integer:
146         return QString::number(qint64(e.value));
147 
148     case QCborValue::Double:
149         return QString::number(e.fpvalue());
150 
151     case QCborValue::ByteArray:
152         return mode == ConversionMode::FromVariantToJson
153                 ? d->stringAt(idx)
154                 : encodeByteArray(d, idx, QCborTag(QCborKnownTags::ExpectedBase64url));
155 
156     case QCborValue::String:
157         return d->stringAt(idx);
158 
159     case QCborValue::Array:
160     case QCborValue::Map:
161 #if defined(QT_JSON_READONLY) || defined(QT_BOOTSTRAPPED)
162         qFatal("Writing JSON is disabled.");
163         return QString();
164 #else
165         return d->valueAt(idx).toDiagnosticNotation(QCborValue::Compact);
166 #endif
167 
168     case QCborValue::SimpleType:
169         break;
170 
171     case QCborValue::False:
172         return QStringLiteral("false");
173 
174     case QCborValue::True:
175         return QStringLiteral("true");
176 
177     case QCborValue::Null:
178         return QStringLiteral("null");
179 
180     case QCborValue::Undefined:
181         return QStringLiteral("undefined");
182 
183     case QCborValue::Invalid:
184         return QString();
185 
186     case QCborValue::Tag:
187     case QCborValue::DateTime:
188     case QCborValue::Url:
189     case QCborValue::RegularExpression:
190     case QCborValue::Uuid:
191         return encodeTag(e.flags & Element::IsContainer ? e.container : nullptr);
192     }
193 
194     // maybe it's a simple type
195     return simpleTypeString(e.type);
196 }
197 
198 QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx,
199                             ConversionMode mode = ConversionMode::FromRaw);
200 
convertExtendedTypeToJson(QCborContainerPrivate * d)201 static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
202 {
203 #ifndef QT_BUILD_QMAKE
204     qint64 tag = d->elements.at(0).value;
205 
206     switch (tag) {
207     case qint64(QCborKnownTags::Url):
208         // use the fullly-encoded URL form
209         if (d->elements.at(1).type == QCborValue::String)
210             return QUrl::fromEncoded(d->byteData(1)->asByteArrayView()).toString(QUrl::FullyEncoded);
211         Q_FALLTHROUGH();
212 
213     case qint64(QCborKnownTags::DateTimeString):
214     case qint64(QCborKnownTags::ExpectedBase64url):
215     case qint64(QCborKnownTags::ExpectedBase64):
216     case qint64(QCborKnownTags::ExpectedBase16):
217     case qint64(QCborKnownTags::Uuid): {
218         // use the string conversion
219         QString s = maybeEncodeTag(d);
220         if (!s.isNull())
221             return s;
222     }
223     }
224 #endif
225 
226     // for all other tags, ignore it and return the converted tagged item
227     return qt_convertToJson(d, 1);
228 }
229 
230 // We need to do this because sub-objects may need conversion.
convertToJsonArray(QCborContainerPrivate * d,ConversionMode mode=ConversionMode::FromRaw)231 static QJsonArray convertToJsonArray(QCborContainerPrivate *d,
232                                      ConversionMode mode = ConversionMode::FromRaw)
233 {
234     QJsonArray a;
235     if (d) {
236         for (qsizetype idx = 0; idx < d->elements.size(); ++idx)
237             a.append(qt_convertToJson(d, idx, mode));
238     }
239     return a;
240 }
241 
242 // We need to do this because the keys need to be sorted and converted to strings
243 // and sub-objects may need recursive conversion.
convertToJsonObject(QCborContainerPrivate * d,ConversionMode mode=ConversionMode::FromRaw)244 static QJsonObject convertToJsonObject(QCborContainerPrivate *d,
245                                        ConversionMode mode = ConversionMode::FromRaw)
246 {
247     QJsonObject o;
248     if (d) {
249         for (qsizetype idx = 0; idx < d->elements.size(); idx += 2)
250             o.insert(makeString(d, idx), qt_convertToJson(d, idx + 1, mode));
251     }
252     return o;
253 }
254 
qt_convertToJson(QCborContainerPrivate * d,qsizetype idx,ConversionMode mode)255 QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx, ConversionMode mode)
256 {
257     // encoding the container itself
258     if (idx == -QCborValue::Array)
259         return convertToJsonArray(d, mode);
260     if (idx == -QCborValue::Map)
261         return convertToJsonObject(d, mode);
262     if (idx < 0) {
263         // tag-like type
264         if (!d || d->elements.size() != 2)
265             return QJsonValue::Undefined;   // invalid state
266         return convertExtendedTypeToJson(d);
267     }
268 
269     // an element in the container
270     const auto &e = d->elements.at(idx);
271     switch (e.type) {
272     case QCborValue::Integer:
273         return QJsonPrivate::Value::fromTrustedCbor(e.value);
274 
275     case QCborValue::ByteArray:
276         if (mode == ConversionMode::FromVariantToJson) {
277             const auto value = makeString(d, idx, mode);
278             return value.isEmpty() ? QJsonValue() : QJsonPrivate::Value::fromTrustedCbor(value);
279         }
280         break;
281     case QCborValue::RegularExpression:
282         if (mode == ConversionMode::FromVariantToJson)
283             return QJsonValue();
284         break;
285     case QCborValue::String:
286     case QCborValue::SimpleType:
287         // make string
288         break;
289 
290     case QCborValue::Array:
291     case QCborValue::Map:
292     case QCborValue::Tag:
293     case QCborValue::DateTime:
294     case QCborValue::Url:
295     case QCborValue::Uuid:
296         // recurse
297         return qt_convertToJson(e.flags & Element::IsContainer ? e.container : nullptr, -e.type,
298                                 mode);
299 
300     case QCborValue::Null:
301     case QCborValue::Undefined:
302     case QCborValue::Invalid:
303         return QJsonValue();
304 
305     case QCborValue::False:
306         return false;
307 
308     case QCborValue::True:
309         return true;
310 
311     case QCborValue::Double:
312         return fpToJson(e.fpvalue());
313     }
314 
315     return QJsonPrivate::Value::fromTrustedCbor(makeString(d, idx, mode));
316 }
317 
318 /*!
319     Converts this QCborValue object to an equivalent representation in JSON and
320     returns it as a QJsonValue.
321 
322     Please note that CBOR contains a richer and wider type set than JSON, so
323     some information may be lost in this conversion. The following table
324     compares CBOR types to JSON types and indicates whether information may be
325     lost or not.
326 
327     \table
328       \header \li CBOR Type     \li JSON Type   \li Comments
329       \row  \li Bool            \li Bool        \li No data loss possible
330       \row  \li Double          \li Number      \li Infinities and NaN will be converted to Null;
331                                                     no data loss for other values
332       \row  \li Integer         \li Number      \li Data loss possible in the conversion if the
333                                                     integer is larger than 2\sup{53} or smaller
334                                                     than -2\sup{53}.
335       \row  \li Null            \li Null        \li No data loss possible
336       \row  \li Undefined       \li Null        \li Type information lost
337       \row  \li String          \li String      \li No data loss possible
338       \row  \li Byte Array      \li String      \li Converted to a lossless encoding like Base64url,
339                                                     but the distinction between strings and byte
340                                                     arrays is lost
341       \row  \li Other simple types \li String   \li Type information lost
342       \row  \li Array           \li Array       \li Conversion applies to each contained value
343       \row  \li Map             \li Object      \li Keys are converted to string; values converted
344                                                     according to this table
345       \row  \li Tags and extended types \li Special \li The tag number itself is lost and the tagged
346                                                     value is converted to JSON
347     \endtable
348 
349     For information on the conversion of CBOR map keys to string, see
350     QCborMap::toJsonObject().
351 
352     If this QCborValue contains the undefined value, this function will return
353     an undefined QJsonValue too. Note that JSON does not support undefined
354     values and undefined QJsonValues are an extension to the specification.
355     They cannot be held in a QJsonArray or QJsonObject, but can be returned
356     from functions to indicate a failure. For all other intents and purposes,
357     they are the same as null.
358 
359     \section3 Special handling of tags and extended types
360 
361     Some tags are handled specially and change the transformation of the tagged
362     value from CBOR to JSON. The following table lists those special cases:
363 
364     \table
365       \header \li Tag           \li CBOR type       \li Transformation
366       \row  \li ExpectedBase64url \li Byte array    \li Encodes the byte array as Base64url
367       \row  \li ExpectedBase64  \li Byte array      \li Encodes the byte array as Base64
368       \row  \li ExpectedBase16  \li Byte array      \li Encodes the byte array as hex
369       \row  \li Url             \li Url and String  \li Uses QUrl::toEncoded() to normalize the
370                                                     encoding to the URL's fully encoded format
371       \row  \li Uuid            \li Uuid and Byte array \li Uses QUuid::toString() to create
372                                                     the string representation
373     \endtable
374 
375     \sa fromJsonValue(), toVariant(), QCborArray::toJsonArray(), QCborMap::toJsonObject()
376  */
toJsonValue() const377 QJsonValue QCborValue::toJsonValue() const
378 {
379     if (container)
380         return qt_convertToJson(container, n < 0 ? -type() : n);
381 
382     // simple values
383     switch (type()) {
384     case False:
385         return false;
386 
387     case Integer:
388         return QJsonPrivate::Value::fromTrustedCbor(n);
389 
390     case True:
391         return true;
392 
393     case Null:
394     case Undefined:
395     case Invalid:
396         return QJsonValue();
397 
398     case Double:
399         return fpToJson(fp_helper());
400 
401     case SimpleType:
402         break;
403 
404     case ByteArray:
405     case String:
406         // empty strings
407         return QJsonValue::String;
408 
409     case Array:
410         // empty array
411         return QJsonArray();
412 
413     case Map:
414         // empty map
415         return QJsonObject();
416 
417     case Tag:
418     case DateTime:
419     case Url:
420     case RegularExpression:
421     case Uuid:
422         // Reachable, but invalid in Json
423         return QJsonValue::Undefined;
424     }
425 
426     return QJsonPrivate::Value::fromTrustedCbor(simpleTypeString(type()));
427 }
428 
toJsonValue() const429 QJsonValue QCborValueRef::toJsonValue() const
430 {
431     return qt_convertToJson(d, i);
432 }
433 
434 /*!
435     Recursively converts every \l QCborValue element in this array to JSON
436     using QCborValue::toJsonValue() and returns the corresponding QJsonArray
437     composed of those elements.
438 
439     Please note that CBOR contains a richer and wider type set than JSON, so
440     some information may be lost in this conversion. For more details on what
441     conversions are applied, see QCborValue::toJsonValue().
442 
443     \sa fromJsonArray(), QCborValue::toJsonValue(), QCborMap::toJsonObject(), toVariantList()
444  */
toJsonArray() const445 QJsonArray QCborArray::toJsonArray() const
446 {
447     return convertToJsonArray(d.data());
448 }
449 
toJsonArray(const QVariantList & list)450 QJsonArray QJsonPrivate::Variant::toJsonArray(const QVariantList &list)
451 {
452     const auto cborArray =
453             QCborContainerPrivate::fromVariantList(list, ConversionMode::FromVariantToJson);
454     return convertToJsonArray(cborArray.d.data(), ConversionMode::FromVariantToJson);
455 }
456 
457 /*!
458     Recursively converts every \l QCborValue value in this map to JSON using
459     QCborValue::toJsonValue() and creates a string key for all keys that aren't
460     strings, then returns the corresponding QJsonObject composed of those
461     associations.
462 
463     Please note that CBOR contains a richer and wider type set than JSON, so
464     some information may be lost in this conversion. For more details on what
465     conversions are applied, see QCborValue::toJsonValue().
466 
467     \section3 Map key conversion to string
468 
469     JSON objects are defined as having string keys, unlike CBOR, so the
470     conversion of a QCborMap to QJsonObject will imply a step of
471     "stringification" of the key values. The conversion will use the special
472     handling of tags and extended types from above and will also convert the
473     rest of the types as follows:
474 
475     \table
476       \header \li Type              \li Transformation
477       \row  \li Bool                \li "true" and "false"
478       \row  \li Null                \li "null"
479       \row  \li Undefined           \li "undefined"
480       \row  \li Integer             \li The decimal string form of the number
481       \row  \li Double              \li The decimal string form of the number
482       \row  \li Byte array          \li Unless tagged differently (see above), encoded as
483                                         Base64url
484       \row  \li Array               \li Replaced by the compact form of its
485                                         \l{QCborValue::toDiagnosticNotation()}{Diagnostic notation}
486       \row  \li Map                 \li Replaced by the compact form of its
487                                         \l{QCborValue::toDiagnosticNotation()}{Diagnostic notation}
488       \row  \li Tags and extended types \li Tag number is dropped and the tagged value is converted
489                                         to string
490     \endtable
491 
492     \sa fromJsonObject(), QCborValue::toJsonValue(), QCborArray::toJsonArray(), toVariantMap()
493  */
toJsonObject() const494 QJsonObject QCborMap::toJsonObject() const
495 {
496     return convertToJsonObject(d.data());
497 }
498 
toJsonObject(const QVariantMap & map)499 QJsonObject QJsonPrivate::Variant::toJsonObject(const QVariantMap &map)
500 {
501     const auto cborMap =
502             QCborContainerPrivate::fromVariantMap(map, ConversionMode::FromVariantToJson);
503     return convertToJsonObject(cborMap.d.data(), ConversionMode::FromVariantToJson);
504 }
505 
506 /*!
507     Converts this value to a native Qt type and returns the corresponding QVariant.
508 
509     The following table lists the mapping performed between \l{Type}{QCborValue
510     types} and \l{QMetaType::Type}{Qt meta types}.
511 
512     \table
513       \header \li CBOR Type         \li Qt or C++ type          \li Notes
514       \row  \li Integer             \li \l qint64               \li
515       \row  \li Double              \li \c double               \li
516       \row  \li Bool                \li \c bool                 \li
517       \row  \li Null                \li \c std::nullptr_t       \li
518       \row  \li Undefined           \li no type (QVariant())    \li
519       \row  \li Byte array          \li \l QByteArray           \li
520       \row  \li String              \li \l QString              \li
521       \row  \li Array               \li \l QVariantList         \li Recursively converts all values
522       \row  \li Map                 \li \l QVariantMap          \li Key types are "stringified"
523       \row  \li Other simple types  \li \l QCborSimpleType      \li
524       \row  \li DateTime            \li \l QDateTime            \li
525       \row  \li Url                 \li \l QUrl                 \li
526       \row  \li RegularExpression   \li \l QRegularExpression   \li
527       \row  \li Uuid                \li \l QUuid                \li
528       \row  \li Other tags          \li Special                 \li The tag is ignored and the tagged
529                                                                     value is converted using this
530                                                                     function
531     \endtable
532 
533     Note that values in both CBOR Maps and Arrays are converted recursively
534     using this function too and placed in QVariantMap and QVariantList instead.
535     You will not find QCborMap and QCborArray stored inside the QVariants.
536 
537     QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
538     to QVariantMap will imply a step of "stringification" of the key values.
539     See QCborMap::toJsonObject() for details.
540 
541     \sa fromVariant(), toJsonValue(), QCborArray::toVariantList(), QCborMap::toVariantMap()
542  */
toVariant() const543 QVariant QCborValue::toVariant() const
544 {
545     switch (type()) {
546     case Integer:
547         return toInteger();
548 
549     case Double:
550         return toDouble();
551 
552     case SimpleType:
553         break;
554 
555     case False:
556     case True:
557         return isTrue();
558 
559     case Null:
560         return QVariant::fromValue(nullptr);
561 
562     case Undefined:
563         return QVariant();
564 
565     case ByteArray:
566         return toByteArray();
567 
568     case String:
569         return toString();
570 
571     case Array:
572         return toArray().toVariantList();
573 
574     case Map:
575         return toMap().toVariantMap();
576 
577     case Tag:
578         // ignore tags
579         return taggedValue().toVariant();
580 
581     case DateTime:
582         return toDateTime();
583 
584 #ifndef QT_BOOTSTRAPPED
585     case Url:
586         return toUrl();
587 #endif
588 
589 #if QT_CONFIG(regularexpression)
590     case RegularExpression:
591         return toRegularExpression();
592 #endif
593 
594     case Uuid:
595         return toUuid();
596 
597     case Invalid:
598         return QVariant();
599 
600     default:
601         break;
602     }
603 
604     if (isSimpleType())
605         return QVariant::fromValue(toSimpleType());
606 
607     Q_UNREACHABLE();
608     return QVariant();
609 }
610 
611 /*!
612     Converts the JSON value contained in \a v into its corresponding CBOR value
613     and returns it. There is no data loss in converting from JSON to CBOR, as
614     the CBOR type set is richer than JSON's. Additionally, values converted to
615     CBOR using this function can be converted back to JSON using toJsonValue()
616     with no data loss.
617 
618     The following table lists the mapping of JSON types to CBOR types:
619 
620     \table
621       \header \li JSON Type     \li CBOR Type
622       \row  \li Bool            \li Bool
623       \row  \li Number          \li Integer (if the number has no fraction and is in the \l qint64
624                                     range) or Double
625       \row  \li String          \li String
626       \row  \li Array           \li Array
627       \row  \li Object          \li Map
628       \row  \li Null            \li Null
629     \endtable
630 
631     \l QJsonValue can also be undefined, indicating a previous operation that
632     failed to complete (for example, searching for a key not present in an
633     object). Undefined values are not JSON types and may not appear in JSON
634     arrays and objects, but this function does return the QCborValue undefined
635     value if the corresponding QJsonValue is undefined.
636 
637     \sa toJsonValue(), fromVariant(), QCborArray::fromJsonArray(), QCborMap::fromJsonObject()
638  */
fromJsonValue(const QJsonValue & v)639 QCborValue QCborValue::fromJsonValue(const QJsonValue &v)
640 {
641     switch (v.type()) {
642     case QJsonValue::Bool:
643         return v.toBool();
644     case QJsonValue::Double: {
645         qint64 i;
646         const double dbl = v.toDouble();
647         if (convertDoubleTo(dbl, &i))
648             return i;
649         return dbl;
650     }
651     case QJsonValue::String:
652         return v.toString();
653     case QJsonValue::Array:
654         return QCborArray::fromJsonArray(v.toArray());
655     case QJsonValue::Object:
656         return QCborMap::fromJsonObject(v.toObject());
657     case QJsonValue::Null:
658         return nullptr;
659     case QJsonValue::Undefined:
660         break;
661     }
662     return QCborValue();
663 }
664 
fromVariantImpl(const QVariant & variant,ConversionMode mode=ConversionMode::FromRaw)665 static QCborValue fromVariantImpl(const QVariant &variant,
666                                   ConversionMode mode = ConversionMode::FromRaw)
667 {
668     switch (variant.userType()) {
669     case QMetaType::UnknownType:
670         return {};
671     case QMetaType::Nullptr:
672         return nullptr;
673     case QMetaType::Bool:
674         return variant.toBool();
675     case QMetaType::Short:
676     case QMetaType::UShort:
677     case QMetaType::Int:
678     case QMetaType::LongLong:
679     case QMetaType::UInt:
680         return variant.toLongLong();
681     case QMetaType::ULongLong:
682         if (mode != ConversionMode::FromVariantToJson )
683             return variant.toLongLong();
684         Q_FALLTHROUGH();
685     case QMetaType::Float:
686     case QMetaType::Double:
687         return variant.toDouble();
688     case QMetaType::QString:
689         return variant.toString();
690     case QMetaType::QStringList:
691         return QCborArray::fromStringList(variant.toStringList());
692     case QMetaType::QByteArray:
693         return variant.toByteArray();
694     case QMetaType::QDateTime:
695         return QCborValue(variant.toDateTime());
696 #ifndef QT_BOOTSTRAPPED
697     case QMetaType::QUrl:
698         return QCborValue(variant.toUrl());
699 #endif
700     case QMetaType::QUuid:
701         return QCborValue(variant.toUuid());
702     case QMetaType::QVariantList:
703         return QCborContainerPrivate::fromVariantList(variant.toList(), mode);
704     case QMetaType::QVariantMap:
705         return QCborContainerPrivate::fromVariantMap(variant.toMap(), mode);
706     case QMetaType::QVariantHash:
707         return QCborMap::fromVariantHash(variant.toHash());
708 #ifndef QT_BOOTSTRAPPED
709 #if QT_CONFIG(regularexpression)
710     case QMetaType::QRegularExpression:
711         return QCborValue(variant.toRegularExpression());
712 #endif
713     case QMetaType::QJsonValue:
714         return QCborValue::fromJsonValue(variant.toJsonValue());
715     case QMetaType::QJsonObject:
716         return QCborMap::fromJsonObject(variant.toJsonObject());
717     case QMetaType::QJsonArray:
718         return QCborArray::fromJsonArray(variant.toJsonArray());
719     case QMetaType::QJsonDocument: {
720         QJsonDocument doc = variant.toJsonDocument();
721         if (doc.isArray())
722             return QCborArray::fromJsonArray(doc.array());
723         return QCborMap::fromJsonObject(doc.object());
724     }
725     case QMetaType::QCborValue:
726         return qvariant_cast<QCborValue>(variant);
727     case QMetaType::QCborArray:
728         return qvariant_cast<QCborArray>(variant);
729     case QMetaType::QCborMap:
730         return qvariant_cast<QCborMap>(variant);
731     case QMetaType::QCborSimpleType:
732         return qvariant_cast<QCborSimpleType>(variant);
733 #endif
734     default:
735         break;
736     }
737 
738     if (variant.isNull())
739         return QCborValue(nullptr);
740 
741     QString string = variant.toString();
742     if (string.isNull())
743         return QCborValue();        // undefined
744     return string;
745 }
746 
appendVariant(QCborContainerPrivate * d,const QVariant & variant,ConversionMode mode=ConversionMode::FromRaw)747 static void appendVariant(QCborContainerPrivate *d, const QVariant &variant,
748                           ConversionMode mode = ConversionMode::FromRaw)
749 {
750     // Handle strings and byte arrays directly, to avoid creating a temporary
751     // dummy container to hold their data.
752     int type = variant.userType();
753     if (type == QMetaType::QString) {
754         d->append(variant.toString());
755     } else if (type == QMetaType::QByteArray) {
756         QByteArray ba = variant.toByteArray();
757         d->appendByteData(ba.constData(), ba.size(), QCborValue::ByteArray);
758     } else {
759         // For everything else, use the function below.
760         d->append(fromVariantImpl(variant, mode));
761     }
762 }
763 
fromVariantMap(const QVariantMap & map,ConversionMode mode)764 QCborMap QCborContainerPrivate::fromVariantMap(const QVariantMap &map, ConversionMode mode)
765 {
766     QCborMap m;
767     m.detach(map.size());
768     QCborContainerPrivate *d = m.d.data();
769 
770     auto it = map.begin();
771     auto end = map.end();
772     for ( ; it != end; ++it) {
773         d->append(it.key());
774         appendVariant(d, it.value(), mode);
775     }
776     return m;
777 }
778 
fromVariantList(const QVariantList & list,ConversionMode mode)779 QCborArray QCborContainerPrivate::fromVariantList(const QVariantList &list, ConversionMode mode)
780 {
781     QCborArray a;
782     a.detach(list.size());
783     for (const QVariant &v : list)
784         appendVariant(a.d.data(), v, mode);
785     return a;
786 }
787 
788 /*!
789     Converts the QVariant \a variant into QCborValue and returns it.
790 
791     QVariants may contain a large list of different meta types, many of which
792     have no corresponding representation in CBOR. That includes all
793     user-defined meta types. When preparing transmission using CBOR, it is
794     suggested to encode carefully each value to prevent loss of representation.
795 
796     The following table lists the conversion this function will apply:
797 
798     \table
799       \header \li Qt (C++) type             \li CBOR type
800       \row  \li invalid (QVariant())        \li Undefined
801       \row  \li \c bool                     \li Bool
802       \row  \li \c std::nullptr_t           \li Null
803       \row  \li \c short, \c ushort, \c int, \c uint, \l qint64  \li Integer
804       \row  \li \l quint64                  \li Integer, but they are cast to \c qint64 first so
805                                                 values higher than 2\sup{63}-1 (\c INT64_MAX) will
806                                                 be wrapped to negative
807       \row  \li \c float, \c double         \li Double
808       \row  \li \l QByteArray               \li ByteArray
809       \row  \li \l QDateTime                \li DateTime
810       \row  \li \l QCborSimpleType          \li Simple type
811       \row  \li \l QJsonArray               \li Array, converted using QCborArray::formJsonArray()
812       \row  \li \l QJsonDocument            \li Array or Map
813       \row  \li \l QJsonObject              \li Map, converted using QCborMap::fromJsonObject()
814       \row  \li \l QJsonValue               \li converted using fromJsonValue()
815       \row  \li \l QRegularExpression       \li RegularExpression
816       \row  \li \l QString                  \li String
817       \row  \li \l QStringList              \li Array
818       \row  \li \l QVariantHash             \li Map
819       \row  \li \l QVariantList             \li Array
820       \row  \li \l QVariantMap              \li Map
821       \row  \li \l QUrl                     \li Url
822       \row  \li \l QUuid                    \li Uuid
823     \endtable
824 
825     If QVariant::isNull() returns true, a null QCborValue is returned or
826     inserted into the list or object, regardless of the type carried by
827     QVariant. Note the behavior change in Qt 6.0 affecting QVariant::isNull()
828     also affects this function.
829 
830     For other types not listed above, a conversion to string will be attempted,
831     usually but not always by calling QVariant::toString(). If the conversion
832     fails the value is replaced by an Undefined CBOR value. Note that
833     QVariant::toString() is also lossy for the majority of types.
834 
835     Please note that the conversions via QVariant::toString() are subject to
836     change at any time. Both QVariant and QCborValue may be extended in the
837     future to support more types, which will result in a change in how this
838     function performs conversions.
839 
840     \sa toVariant(), fromJsonValue(), QCborArray::toVariantList(), QCborMap::toVariantMap(), QJsonValue::fromVariant()
841  */
fromVariant(const QVariant & variant)842 QCborValue QCborValue::fromVariant(const QVariant &variant)
843 {
844     return fromVariantImpl(variant);
845 }
846 
847 /*!
848     Recursively converts each \l QCborValue in this array using
849     QCborValue::toVariant() and returns the QVariantList composed of the
850     converted items.
851 
852     Conversion to \l QVariant is not completely lossless. Please see the
853     documentation in QCborValue::toVariant() for more information.
854 
855     \sa fromVariantList(), fromStringList(), toJsonArray(),
856         QCborValue::toVariant(), QCborMap::toVariantMap()
857  */
toVariantList() const858 QVariantList QCborArray::toVariantList() const
859 {
860     QVariantList retval;
861     retval.reserve(size());
862     for (qsizetype i = 0; i < size(); ++i)
863         retval.append(d->valueAt(i).toVariant());
864     return retval;
865 }
866 
867 /*!
868     Returns a QCborArray containing all the strings found in the \a list list.
869 
870     \sa fromVariantList(), fromJsonArray()
871  */
fromStringList(const QStringList & list)872 QCborArray QCborArray::fromStringList(const QStringList &list)
873 {
874     QCborArray a;
875     a.detach(list.size());
876     for (const QString &s : list)
877         a.d->append(s);
878     return a;
879 }
880 
881 /*!
882     Converts all the items in the \a list to CBOR using
883     QCborValue::fromVariant() and returns the array composed of those elements.
884 
885     Conversion from \l QVariant is not completely lossless. Please see the
886     documentation in QCborValue::fromVariant() for more information.
887 
888     \sa toVariantList(), fromStringList(), fromJsonArray(), QCborMap::fromVariantMap()
889  */
fromVariantList(const QVariantList & list)890 QCborArray QCborArray::fromVariantList(const QVariantList &list)
891 {
892     return QCborContainerPrivate::fromVariantList(list);
893 }
894 
895 /*!
896     Converts all JSON items found in the \a array array to CBOR using
897     QCborValue::fromJson(), and returns the CBOR array composed of those
898     elements.
899 
900     This conversion is lossless, as the CBOR type system is a superset of
901     JSON's. Moreover, the array returned by this function can be converted back
902     to the original \a array by using toJsonArray().
903 
904     \sa toJsonArray(), toVariantList(), QCborValue::fromJsonValue(), QCborMap::fromJsonObject()
905  */
fromJsonArray(const QJsonArray & array)906 QCborArray QCborArray::fromJsonArray(const QJsonArray &array)
907 {
908     QCborArray result;
909     result.d = array.a;
910     return result;
911 }
912 
913 /*!
914     Converts the CBOR values to QVariant using QCborValue::toVariant() and
915     "stringifies" all the CBOR keys in this map, returning the QVariantMap that
916     results from that association list.
917 
918     QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
919     to QVariantMap will imply a step of "stringification" of the key values.
920     See QCborMap::toJsonObject() for details.
921 
922     In addition, the conversion to \l QVariant is not completely lossless.
923     Please see the documentation in QCborValue::toVariant() for more
924     information.
925 
926     \sa fromVariantMap(), toVariantHash(), toJsonObject(), QCborValue::toVariant(),
927         QCborArray::toVariantList()
928  */
toVariantMap() const929 QVariantMap QCborMap::toVariantMap() const
930 {
931     QVariantMap retval;
932     for (qsizetype i = 0; i < 2 * size(); i += 2)
933         retval.insert(makeString(d.data(), i), d->valueAt(i + 1).toVariant());
934     return retval;
935 }
936 
937 /*!
938     Converts the CBOR values to QVariant using QCborValue::toVariant() and
939     "stringifies" all the CBOR keys in this map, returning the QVariantHash that
940     results from that association list.
941 
942     QVariantMaps have string keys, unlike CBOR, so the conversion of a QCborMap
943     to QVariantMap will imply a step of "stringification" of the key values.
944     See QCborMap::toJsonObject() for details.
945 
946     In addition, the conversion to \l QVariant is not completely lossless.
947     Please see the documentation in QCborValue::toVariant() for more
948     information.
949 
950     \sa fromVariantHash(), toVariantMap(), toJsonObject(), QCborValue::toVariant(),
951         QCborArray::toVariantList()
952  */
toVariantHash() const953 QVariantHash QCborMap::toVariantHash() const
954 {
955     QVariantHash retval;
956     retval.reserve(size());
957     for (qsizetype i = 0; i < 2 * size(); i += 2)
958         retval.insert(makeString(d.data(), i), d->valueAt(i + 1).toVariant());
959     return retval;
960 }
961 
962 /*!
963     Converts all the items in \a map to CBOR using QCborValue::fromVariant()
964     and returns the map composed of those elements.
965 
966     Conversion from \l QVariant is not completely lossless. Please see the
967     documentation in QCborValue::fromVariant() for more information.
968 
969     \sa toVariantMap(), fromVariantHash(), fromJsonObject(), QCborValue::fromVariant()
970  */
fromVariantMap(const QVariantMap & map)971 QCborMap QCborMap::fromVariantMap(const QVariantMap &map)
972 {
973     return QCborContainerPrivate::fromVariantMap(map);
974 }
975 
976 /*!
977     Converts all the items in \a hash to CBOR using QCborValue::fromVariant()
978     and returns the map composed of those elements.
979 
980     Conversion from \l QVariant is not completely lossless. Please see the
981     documentation in QCborValue::fromVariant() for more information.
982 
983     \sa toVariantHash(), fromVariantMap(), fromJsonObject(), QCborValue::fromVariant()
984  */
fromVariantHash(const QVariantHash & hash)985 QCborMap QCborMap::fromVariantHash(const QVariantHash &hash)
986 {
987     QCborMap m;
988     m.detach(hash.size());
989     QCborContainerPrivate *d = m.d.data();
990 
991     auto it = hash.begin();
992     auto end = hash.end();
993     for ( ; it != end; ++it) {
994         d->append(it.key());
995         appendVariant(d, it.value());
996     }
997     return m;
998 }
999 
1000 /*!
1001     Converts all JSON items found in the \a obj object to CBOR using
1002     QCborValue::fromJson(), and returns the map composed of those elements.
1003 
1004     This conversion is lossless, as the CBOR type system is a superset of
1005     JSON's. Moreover, the map returned by this function can be converted back
1006     to the original \a obj by using toJsonObject().
1007 
1008     \sa toJsonObject(), toVariantMap(), QCborValue::fromJsonValue(), QCborArray::fromJsonArray()
1009  */
fromJsonObject(const QJsonObject & obj)1010 QCborMap QCborMap::fromJsonObject(const QJsonObject &obj)
1011 {
1012     QCborMap result;
1013     result.d = obj.o;
1014     return result;
1015 }
1016 
1017 QT_END_NAMESPACE
1018