summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qnumeric_p.h52
-rw-r--r--src/corelib/serialization/qcbordiagnostic.cpp281
-rw-r--r--src/corelib/serialization/qcborvalue.cpp225
-rw-r--r--src/corelib/serialization/qcborvalue_p.h2
-rw-r--r--src/corelib/serialization/serialization.pri1
-rw-r--r--tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp165
-rw-r--r--tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp8
7 files changed, 505 insertions, 229 deletions
diff --git a/src/corelib/global/qnumeric_p.h b/src/corelib/global/qnumeric_p.h
index 5f8a124bcc..9c8514f5a3 100644
--- a/src/corelib/global/qnumeric_p.h
+++ b/src/corelib/global/qnumeric_p.h
@@ -163,6 +163,58 @@ Q_DECL_CONST_FUNCTION static inline bool qt_is_finite(float f)
#ifndef Q_CLANG_QDOC
namespace {
+/*!
+ 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 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
+}
+
// Overflow math.
// This provides efficient implementations for int, unsigned, qsizetype and
// size_t. Implementations for 8- and 16-bit types will work but may not be as
diff --git a/src/corelib/serialization/qcbordiagnostic.cpp b/src/corelib/serialization/qcbordiagnostic.cpp
new file mode 100644
index 0000000000..8e641ff7cf
--- /dev/null
+++ b/src/corelib/serialization/qcbordiagnostic.cpp
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** 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 <private/qnumeric_p.h>
+#include <qstack.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+class DiagnosticNotation
+{
+public:
+ static QString create(const QCborValue &v, QCborValue::DiagnosticNotationOptions opts)
+ {
+ DiagnosticNotation dn(opts);
+ dn.appendValue(v);
+ return dn.result;
+ }
+
+private:
+ QStack<int> byteArrayFormatStack;
+ QString separator;
+ QString result;
+ QCborValue::DiagnosticNotationOptions opts;
+ int nestingLevel = 0;
+
+ struct Nest {
+ enum { IndentationWidth = 4 };
+ DiagnosticNotation *dn;
+ Nest(DiagnosticNotation *that) : dn(that)
+ {
+ ++dn->nestingLevel;
+ static const char indent[IndentationWidth + 1] = " ";
+ if (dn->opts & QCborValue::LineWrapped)
+ dn->separator += QLatin1String(indent, IndentationWidth);
+ }
+ ~Nest()
+ {
+ --dn->nestingLevel;
+ if (dn->opts & QCborValue::LineWrapped)
+ dn->separator.chop(IndentationWidth);
+ }
+ };
+
+ DiagnosticNotation(QCborValue::DiagnosticNotationOptions opts_)
+ : separator(QLatin1String(opts_ & QCborValue::LineWrapped ? "\n" : "")), opts(opts_)
+ {
+ byteArrayFormatStack.push(int(QCborKnownTags::ExpectedBase16));
+ }
+
+ void appendString(const QString &s);
+ void appendArray(const QCborArray &a);
+ void appendMap(const QCborMap &m);
+ void appendValue(const QCborValue &v);
+};
+}
+
+static QString 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;
+}
+
+static bool isByteArrayEncodingTag(QCborTag tag)
+{
+ switch (quint64(tag)) {
+ case quint64(QCborKnownTags::ExpectedBase16):
+ case quint64(QCborKnownTags::ExpectedBase64):
+ case quint64(QCborKnownTags::ExpectedBase64url):
+ return true;
+ }
+ return false;
+}
+
+void DiagnosticNotation::appendString(const QString &s)
+{
+ result += QLatin1Char('"')
+ + QString(s)
+ .replace(QLatin1Char('\\'), QLatin1String("\\\\"))
+ .replace(QLatin1Char('"'), QLatin1String("\\\""))
+ + QLatin1Char('"');
+}
+
+void DiagnosticNotation::appendArray(const QCborArray &a)
+{
+ result += QLatin1Char('[');
+
+ // length 2 (including the space) when not line wrapping
+ QLatin1String commaValue(", ", opts & QCborValue::LineWrapped ? 1 : 2);
+ {
+ Nest n(this);
+ QLatin1String comma;
+ for (auto v : a) {
+ result += comma + separator;
+ comma = commaValue;
+ appendValue(v);
+ }
+ }
+
+ result += separator + QLatin1Char(']');
+}
+
+void DiagnosticNotation::appendMap(const QCborMap &m)
+{
+ result += QLatin1Char('{');
+
+ // length 2 (including the space) when not line wrapping
+ QLatin1String commaValue(", ", opts & QCborValue::LineWrapped ? 1 : 2);
+ {
+ Nest n(this);
+ QLatin1String comma;
+ for (auto v : m) {
+ result += comma + separator;
+ comma = commaValue;
+ appendValue(v.first);
+ result += QLatin1String(": ");
+ appendValue(v.second);
+ }
+ }
+
+ result += separator + QLatin1Char('}');
+};
+
+void DiagnosticNotation::appendValue(const QCborValue &v)
+{
+ switch (v.type()) {
+ case QCborValue::Integer:
+ result += QString::number(v.toInteger());
+ return;
+ case QCborValue::ByteArray:
+ switch (byteArrayFormatStack.top()) {
+ case int(QCborKnownTags::ExpectedBase16):
+ result += QString::fromLatin1("h'" +
+ v.toByteArray().toHex(opts & QCborValue::ExtendedFormat ? ' ' : '\0') +
+ '\'');
+ return;
+ case int(QCborKnownTags::ExpectedBase64):
+ result += QString::fromLatin1("b64'" + v.toByteArray().toBase64() + '\'');
+ return;
+ default:
+ case int(QCborKnownTags::ExpectedBase64url):
+ result += QString::fromLatin1("b64'" +
+ v.toByteArray().toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals) +
+ '\'');
+ return;
+ }
+ case QCborValue::String:
+ return appendString(v.toString());
+ case QCborValue::Array:
+ return appendArray(v.toArray());
+ case QCborValue::Map:
+ return appendMap(v.toMap());
+ case QCborValue::False:
+ result += QLatin1String("false");
+ return;
+ case QCborValue::True:
+ result += QLatin1String("true");
+ return;
+ case QCborValue::Null:
+ result += QLatin1String("null");
+ return;
+ case QCborValue::Undefined:
+ result += QLatin1String("undefined");
+ return;
+ case QCborValue::Double:
+ result += makeFpString(v.toDouble());
+ return;
+ case QCborValue::Invalid:
+ result += QStringLiteral("<invalid>");
+ return;
+
+ default:
+ // Only tags, extended types, and simple types remain; see below.
+ break;
+ }
+
+ if (v.isTag()) {
+ // We handle all extended types as regular tags, so it won't matter
+ // whether we understand that tag or not.
+ bool byteArrayFormat = opts & QCborValue::ExtendedFormat && isByteArrayEncodingTag(v.tag());
+ if (byteArrayFormat)
+ byteArrayFormatStack.push(int(v.tag()));
+ result += QString::number(quint64(v.tag())) + QLatin1Char('(');
+ appendValue(v.taggedValue());
+ result += QLatin1Char(')');
+ if (byteArrayFormat)
+ byteArrayFormatStack.pop();
+ } else {
+ // must be a simple type
+ result += QString::fromLatin1("simple(%1)").arg(quint8(v.toSimpleType()));
+ }
+}
+
+/*!
+ Creates the diagnostic notation equivalent of this CBOR object and returns
+ 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);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp
index 0ec84c12d1..4108418550 100644
--- a/src/corelib/serialization/qcborvalue.cpp
+++ b/src/corelib/serialization/qcborvalue.cpp
@@ -47,9 +47,7 @@
#include <qendian.h>
#include <qlocale.h>
#include <private/qnumeric_p.h>
-#include <qscopedvaluerollback.h>
#include <private/qsimd_p.h>
-#include <qstack.h>
#include <new>
@@ -768,58 +766,6 @@ 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)) {
@@ -869,146 +815,6 @@ static inline int typeOrder(Element e1, Element e2)
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::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::Invalid:
- return QStringLiteral("<invalid>");
-
- case QCborValue::Tag:
- case QCborValue::SimpleType:
- default: // tags and other simple types that are recognized
- break; // are all handled below
- }
-
- if (v.isTag()) {
- 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;
- }
-
- // must be a simple type
- return QString::fromLatin1("simple(%1)").arg(quint8(v.toSimpleType()));
-}
-
QCborContainerPrivate::~QCborContainerPrivate()
{
// delete our elements
@@ -2508,37 +2314,6 @@ Q_NEVER_INLINE void QCborValue::toCbor(QCborStreamWriter &writer, EncodingOption
}
}
-/*!
- 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);
diff --git a/src/corelib/serialization/qcborvalue_p.h b/src/corelib/serialization/qcborvalue_p.h
index 02aa05bcdb..3a28707056 100644
--- a/src/corelib/serialization/qcborvalue_p.h
+++ b/src/corelib/serialization/qcborvalue_p.h
@@ -56,6 +56,8 @@
#include <private/qglobal_p.h>
#include <private/qutfcodec_p.h>
+#include <math.h>
+
QT_BEGIN_NAMESPACE
namespace QtCbor {
diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri
index 73b99b8e64..4f2dc64e4f 100644
--- a/src/corelib/serialization/serialization.pri
+++ b/src/corelib/serialization/serialization.pri
@@ -24,6 +24,7 @@ HEADERS += \
SOURCES += \
serialization/qcborstream.cpp \
+ serialization/qcbordiagnostic.cpp \
serialization/qcborvalue.cpp \
serialization/qdatastream.cpp \
serialization/qjson.cpp \
diff --git a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp
index 8ab43f9fc9..e469bffa4f 100644
--- a/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp
+++ b/tests/auto/corelib/serialization/qcborvalue/tst_qcborvalue.cpp
@@ -95,6 +95,8 @@ private slots:
void fromCbor();
void validation_data();
void validation();
+ void toDiagnosticNotation_data();
+ void toDiagnosticNotation();
};
// Get the validation data from TinyCBOR (see src/3rdparty/tinycbor/tests/parser/data.cpp)
@@ -1517,6 +1519,169 @@ void tst_QCborValue::validation()
}
}
+void tst_QCborValue::toDiagnosticNotation_data()
+{
+ QTest::addColumn<QCborValue>("v");
+ QTest::addColumn<int>("opts");
+ QTest::addColumn<QString>("expected");
+ QDateTime dt = QDateTime::currentDateTimeUtc();
+ QUuid uuid = QUuid::createUuid();
+
+ QMetaEnum me = QMetaEnum::fromType<QCborValue::Type>();
+ auto st = [](QCborSimpleType t) { return QVariant::fromValue<SimpleTypeWrapper>(t); };
+ auto add = [me](const QCborValue &v, const QString &exp) {
+ auto addRow = [=](const char *prefix) -> QTestData & {
+ QCborValue::Type t = v.type();
+ if (t == QCborValue::Integer)
+ return QTest::addRow("%sInteger:%lld", prefix, v.toInteger());
+ if (t == QCborValue::Double)
+ return QTest::addRow("%sDouble:%g", prefix, v.toDouble());
+ if (t == QCborValue::ByteArray)
+ return QTest::addRow("%sByteArray:%d", prefix, v.toByteArray().size());
+ if (t == QCborValue::String)
+ return QTest::addRow("%sString:%d", prefix, v.toString().size());
+
+ QByteArray typeString = me.valueToKey(t);
+ Q_ASSERT(!typeString.isEmpty());
+ return QTest::newRow(prefix + typeString);
+ };
+ addRow("") << v << int(QCborValue::DiagnosticNotationOptions{}) << exp;
+ addRow("LW:") << v << int(QCborValue::LineWrapped) << exp;
+ addRow("Array:") << QCborValue(QCborArray{v}) << int(QCborValue::DiagnosticNotationOptions{}) << '[' + exp + ']';
+ addRow("Mapped:") << QCborValue(QCborMap{{2, v}}) << int(QCborValue::DiagnosticNotationOptions{}) << "{2: " + exp + '}';
+ addRow("Mapping:") << QCborValue(QCborMap{{v, 2}}) << int(QCborValue::DiagnosticNotationOptions{}) << '{' + exp + ": 2}";
+ };
+
+ // empty arrays and maps
+ QTest::newRow("EmptyArray")
+ << QCborValue(QCborArray()) << int(QCborValue::DiagnosticNotationOptions{})
+ << "[]";
+ QTest::newRow("EmptyMap")
+ << QCborValue(QCborMap()) << int(QCborValue::DiagnosticNotationOptions{})
+ << "{}";
+
+ add(QCborValue(), "undefined");
+ add(QCborValue::Null, "null");
+ add(false, "false");
+ add(true, "true");
+ add(QCborSimpleType(0), "simple(0)");
+ QTest::newRow("SimpleType-255")
+ << QCborValue(QCborSimpleType(255)) << int(QCborValue::DiagnosticNotationOptions{})
+ << "simple(255)";
+ add(0, "0");
+ add(1, "1");
+ add(-1, "-1");
+ add(std::numeric_limits<qint64>::min(), QString::number(std::numeric_limits<qint64>::min()));
+ add(std::numeric_limits<qint64>::max(), QString::number(std::numeric_limits<qint64>::max()));
+ add(0., "0.0");
+ add(1.25, "1.25");
+ add(-1.25, "-1.25");
+ add(qInf(), "inf");
+ add(-qInf(), "-inf");
+ add(qQNaN(), "nan");
+ add(QByteArray(), "h''");
+ add(QByteArray("Hello"), "h'48656c6c6f'");
+ add(QLatin1String(), QLatin1String("\"\""));
+ add("Hello", "\"Hello\"");
+ add("\"Hello\\World\"", "\"\\\"Hello\\\\World\\\"\"");
+ add(QCborValue(dt), "0(\"" + dt.toString(Qt::ISODateWithMs) + "\")");
+ add(QCborValue(QUrl("http://example.com")), "32(\"http://example.com\")");
+ add(QCborValue(QRegularExpression("^.*$")), "35(\"^.*$\")");
+ add(QCborValue(uuid), "37(h'" + uuid.toString(QUuid::Id128) + "')");
+
+ // arrays and maps with more than one element
+ QTest::newRow("2Array")
+ << QCborValue(QCborArray{0, 1}) << int(QCborValue::DiagnosticNotationOptions{})
+ << "[0, 1]";
+ QTest::newRow("2Map")
+ << QCborValue(QCborMap{{0, 1}, {"foo", "bar"}}) << int(QCborValue::DiagnosticNotationOptions{})
+ << "{0: 1, \"foo\": \"bar\"}";
+
+ // line wrapping in arrays and maps
+ QTest::newRow("LW:EmptyArray")
+ << QCborValue(QCborArray()) << int(QCborValue::LineWrapped)
+ << "[\n]";
+ QTest::newRow("LW:EmptyMap")
+ << QCborValue(QCborMap()) << int(QCborValue::LineWrapped)
+ << "{\n}";
+ QTest::newRow("LW:Array:Integer:0")
+ << QCborValue(QCborArray{0}) << int(QCborValue::LineWrapped)
+ << "[\n 0\n]";
+ QTest::newRow("LW:Array:String:5")
+ << QCborValue(QCborArray{"Hello"}) << int(QCborValue::LineWrapped)
+ << "[\n \"Hello\"\n]";
+ QTest::newRow("LW:Map:0-0")
+ << QCborValue(QCborMap{{0, 0}}) << int(QCborValue::LineWrapped)
+ << "{\n 0: 0\n}";
+ QTest::newRow("LW:Map:String:5")
+ << QCborValue(QCborMap{{0, "Hello"}}) << int(QCborValue::LineWrapped)
+ << "{\n 0: \"Hello\"\n}";
+ QTest::newRow("LW:2Array")
+ << QCborValue(QCborArray{0, 1}) << int(QCborValue::LineWrapped)
+ << "[\n 0,\n 1\n]";
+ QTest::newRow("LW:2Map")
+ << QCborValue(QCborMap{{0, 0}, {"foo", "bar"}}) << int(QCborValue::LineWrapped)
+ << "{\n 0: 0,\n \"foo\": \"bar\"\n}";
+
+ // nested arrays and maps
+ QTest::newRow("Array:EmptyArray")
+ << QCborValue(QCborArray() << QCborArray()) << int(QCborValue::DiagnosticNotationOptions{})
+ << "[[]]";
+ QTest::newRow("Array:EmptyMap")
+ << QCborValue(QCborArray() << QCborMap()) << int(QCborValue::DiagnosticNotationOptions{})
+ << "[{}]";
+ QTest::newRow("LW:Array:EmptyArray")
+ << QCborValue(QCborArray() << QCborArray()) << int(QCborValue::LineWrapped)
+ << "[\n [\n ]\n]";
+ QTest::newRow("LW:Array:EmptyMap")
+ << QCborValue(QCborArray() << QCborMap()) << int(QCborValue::LineWrapped)
+ << "[\n {\n }\n]";
+ QTest::newRow("LW:Array:2Array")
+ << QCborValue(QCborArray() << QCborArray{0, 1}) << int(QCborValue::LineWrapped)
+ << "[\n [\n 0,\n 1\n ]\n]";
+ QTest::newRow("LW:Map:2Array")
+ << QCborValue(QCborMap{{0, QCborArray{0, 1}}}) << int(QCborValue::LineWrapped)
+ << "{\n 0: [\n 0,\n 1\n ]\n}";
+ QTest::newRow("LW:Map:2Map")
+ << QCborValue(QCborMap{{-1, QCborMap{{0, 0}, {"foo", "bar"}}}}) << int(QCborValue::LineWrapped)
+ << "{\n -1: {\n 0: 0,\n \"foo\": \"bar\"\n }\n}";
+
+ // extended formatting for byte arrays
+ QTest::newRow("Extended:ByteArray:0")
+ << QCborValue(QByteArray()) << int(QCborValue::ExtendedFormat)
+ << "h''";
+ QTest::newRow("Extended:ByteArray:5")
+ << QCborValue(QByteArray("Hello")) << int(QCborValue::ExtendedFormat)
+ << "h'48 65 6c 6c 6f'";
+ QTest::newRow("Extended:ByteArray:Base64url")
+ << QCborValue(QCborKnownTags::ExpectedBase64url, QByteArray("\xff\xef"))
+ << int(QCborValue::ExtendedFormat) << "21(b64'_-8')";
+ QTest::newRow("Extended:ByteArray:Base64")
+ << QCborValue(QCborKnownTags::ExpectedBase64, QByteArray("\xff\xef"))
+ << int(QCborValue::ExtendedFormat) << "22(b64'/+8=')";
+
+ // formatting applies through arrays too
+ QTest::newRow("Extended:Array:ByteArray:Base64url")
+ << QCborValue(QCborKnownTags::ExpectedBase64url, QCborArray{QByteArray("\xff\xef")})
+ << int(QCborValue::ExtendedFormat) << "21([b64'_-8'])";
+ // and only the innermost applies
+ QTest::newRow("ByteArray:multiple-tags")
+ << QCborValue(QCborKnownTags::ExpectedBase64url,
+ QCborArray{QCborValue(QCborKnownTags::ExpectedBase16, QByteArray("Hello")),
+ QByteArray("\xff\xef")})
+ << int(QCborValue::ExtendedFormat) << "21([23(h'48 65 6c 6c 6f'), b64'_-8'])";
+}
+
+void tst_QCborValue::toDiagnosticNotation()
+{
+ QFETCH(QCborValue, v);
+ QFETCH(QString, expected);
+ QFETCH(int, opts);
+
+ QString result = v.toDiagnosticNotation(QCborValue::DiagnosticNotationOptions(opts));
+ QCOMPARE(result, expected);
+}
+
QTEST_MAIN(tst_QCborValue)
#include "tst_qcborvalue.moc"
diff --git a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp
index ad4c48450a..56245a7173 100644
--- a/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp
+++ b/tests/auto/corelib/serialization/qcborvalue_json/tst_qcborvalue_json.cpp
@@ -300,12 +300,12 @@ void tst_QCborValue_Json::nonStringKeysInMaps_data()
QTest::newRow("base64") << QCborValue(QCborKnownTags::ExpectedBase64, data) << "/wE=";
QTest::newRow("hex") << QCborValue(QCborKnownTags::ExpectedBase16, data) << "ff01";
- QTest::newRow("emptyarray") << QCborValue(QCborValue::Array) << "[ ]";
- QTest::newRow("emptymap") << QCborValue(QCborValue::Map) << "{ }";
+ QTest::newRow("emptyarray") << QCborValue(QCborValue::Array) << "[]";
+ QTest::newRow("emptymap") << QCborValue(QCborValue::Map) << "{}";
QTest::newRow("array") << QCborValue(QCborArray{1, true, 2.5, "Hello"})
- << "[ 1, true, 2.5, \"Hello\" ]";
+ << "[1, true, 2.5, \"Hello\"]";
QTest::newRow("map") << QCborValue(QCborMap{{"Hello", 0}, {0, "Hello"}})
- << "{ \"Hello\": 0, 0: \"Hello\" }";
+ << "{\"Hello\": 0, 0: \"Hello\"}";
QDateTime dt = QDateTime::currentDateTimeUtc();
QUrl url("https://example.com");