QCborValue: Extend the constructor to also create extended types
We already did that when parsing from CBOR binary data, so the code was already present. [ChangeLog][QtCore][QCborValue] The constructor taking a CBOR tag and a value to be tagged now attempts to convert to a QCborValue extended type. For example, if the tag is 0 (UnixTime_t) and the payload is a number, the resulting object will become tag 1 (DateTime) and the payload will be the the ISO-8601 date/time string. Fixes: QTBUG-79196 Change-Id: I6edce5101800424a8093fffd15cdf650fb2fc45c Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>bb10
parent
8027fb60dd
commit
bcbefcd645
|
|
@ -758,6 +758,81 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
using namespace QtCbor;
|
||||
|
||||
static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d)
|
||||
{
|
||||
qint64 tag = d->elements.at(0).value;
|
||||
auto &e = d->elements[1];
|
||||
const ByteData *b = d->byteData(e);
|
||||
|
||||
auto replaceByteData = [&](const char *buf, qsizetype len, Element::ValueFlags f) {
|
||||
d->data.clear();
|
||||
d->usedData = 0;
|
||||
e.flags = Element::HasByteData | f;
|
||||
e.value = d->addByteData(buf, len);
|
||||
};
|
||||
|
||||
switch (tag) {
|
||||
case qint64(QCborKnownTags::DateTimeString):
|
||||
case qint64(QCborKnownTags::UnixTime_t): {
|
||||
QDateTime dt;
|
||||
if (tag == qint64(QCborKnownTags::DateTimeString) && b &&
|
||||
e.type == QCborValue::String && (e.flags & Element::StringIsUtf16) == 0) {
|
||||
// The data is supposed to be US-ASCII. If it isn't (contains UTF-8),
|
||||
// QDateTime::fromString will fail anyway.
|
||||
dt = QDateTime::fromString(b->asLatin1(), Qt::ISODateWithMs);
|
||||
} else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Integer) {
|
||||
dt = QDateTime::fromSecsSinceEpoch(e.value, Qt::UTC);
|
||||
} else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Double) {
|
||||
dt = QDateTime::fromMSecsSinceEpoch(qint64(e.fpvalue() * 1000), Qt::UTC);
|
||||
}
|
||||
if (dt.isValid()) {
|
||||
QByteArray text = dt.toString(Qt::ISODateWithMs).toLatin1();
|
||||
replaceByteData(text, text.size(), Element::StringIsAscii);
|
||||
e.type = QCborValue::String;
|
||||
d->elements[0].value = qint64(QCborKnownTags::DateTimeString);
|
||||
return QCborValue::DateTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case qint64(QCborKnownTags::Url):
|
||||
if (e.type == QCborValue::String) {
|
||||
if (b) {
|
||||
// normalize to a short (decoded) form, so as to save space
|
||||
QUrl url(e.flags & Element::StringIsUtf16 ?
|
||||
b->asQStringRaw() :
|
||||
b->toUtf8String());
|
||||
QByteArray encoded = url.toString(QUrl::DecodeReserved).toUtf8();
|
||||
replaceByteData(encoded, encoded.size(), {});
|
||||
}
|
||||
return QCborValue::Url;
|
||||
}
|
||||
break;
|
||||
|
||||
case quint64(QCborKnownTags::RegularExpression):
|
||||
if (e.type == QCborValue::String) {
|
||||
// no normalization is necessary
|
||||
return QCborValue::RegularExpression;
|
||||
}
|
||||
break;
|
||||
|
||||
case qint64(QCborKnownTags::Uuid):
|
||||
if (e.type == QCborValue::ByteArray) {
|
||||
// force the size to 16
|
||||
char buf[sizeof(QUuid)] = {};
|
||||
if (b)
|
||||
memcpy(buf, b->byte(), qMin(sizeof(buf), size_t(b->len)));
|
||||
replaceByteData(buf, sizeof(buf), {});
|
||||
|
||||
return QCborValue::Uuid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// no enriching happened
|
||||
return QCborValue::Tag;
|
||||
}
|
||||
|
||||
// in qcborstream.cpp
|
||||
extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error);
|
||||
|
||||
|
|
@ -1384,77 +1459,10 @@ static QCborValue taggedValueFromCbor(QCborStreamReader &reader)
|
|||
d->decodeValueFromCbor(reader);
|
||||
}
|
||||
|
||||
QCborValue::Type type = QCborValue::Tag;
|
||||
QCborValue::Type type;
|
||||
if (reader.lastError() == QCborError::NoError) {
|
||||
// post-process to create our extended types
|
||||
qint64 tag = d->elements.at(0).value;
|
||||
auto &e = d->elements[1];
|
||||
const ByteData *b = d->byteData(e);
|
||||
|
||||
auto replaceByteData = [&](const char *buf, qsizetype len, Element::ValueFlags f) {
|
||||
d->data.clear();
|
||||
d->usedData = 0;
|
||||
e.flags = Element::HasByteData | f;
|
||||
e.value = d->addByteData(buf, len);
|
||||
};
|
||||
|
||||
switch (tag) {
|
||||
case qint64(QCborKnownTags::DateTimeString):
|
||||
case qint64(QCborKnownTags::UnixTime_t): {
|
||||
QDateTime dt;
|
||||
if (tag == qint64(QCborKnownTags::DateTimeString) && b &&
|
||||
e.type == QCborValue::String && (e.flags & Element::StringIsUtf16) == 0) {
|
||||
// The data is supposed to be US-ASCII. If it isn't,
|
||||
// QDateTime::fromString will fail anyway.
|
||||
dt = QDateTime::fromString(b->asLatin1(), Qt::ISODateWithMs);
|
||||
} else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Integer) {
|
||||
dt = QDateTime::fromSecsSinceEpoch(e.value, Qt::UTC);
|
||||
} else if (tag == qint64(QCborKnownTags::UnixTime_t) && e.type == QCborValue::Double) {
|
||||
dt = QDateTime::fromMSecsSinceEpoch(qint64(e.fpvalue() * 1000), Qt::UTC);
|
||||
}
|
||||
if (dt.isValid()) {
|
||||
QByteArray text = dt.toString(Qt::ISODateWithMs).toLatin1();
|
||||
replaceByteData(text, text.size(), Element::StringIsAscii);
|
||||
e.type = QCborValue::String;
|
||||
d->elements[0].value = qint64(QCborKnownTags::DateTimeString);
|
||||
type = QCborValue::DateTime;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case qint64(QCborKnownTags::Url):
|
||||
if (e.type == QCborValue::String) {
|
||||
if (b) {
|
||||
// normalize to a short (decoded) form, so as to save space
|
||||
QUrl url(e.flags & Element::StringIsUtf16 ?
|
||||
b->asQStringRaw() :
|
||||
b->toUtf8String());
|
||||
QByteArray encoded = url.toString(QUrl::DecodeReserved).toUtf8();
|
||||
replaceByteData(encoded, encoded.size(), {});
|
||||
}
|
||||
type = QCborValue::Url;
|
||||
}
|
||||
break;
|
||||
|
||||
case quint64(QCborKnownTags::RegularExpression):
|
||||
if (e.type == QCborValue::String) {
|
||||
// no normalization is necessary
|
||||
type = QCborValue::RegularExpression;
|
||||
}
|
||||
break;
|
||||
|
||||
case qint64(QCborKnownTags::Uuid):
|
||||
if (e.type == QCborValue::ByteArray) {
|
||||
// force the size to 16
|
||||
char buf[sizeof(QUuid)] = {};
|
||||
if (b)
|
||||
memcpy(buf, b->byte(), qMin(sizeof(buf), size_t(b->len)));
|
||||
replaceByteData(buf, sizeof(buf), {});
|
||||
|
||||
type = QCborValue::Uuid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
type = convertToExtendedType(d);
|
||||
} else {
|
||||
// decoding error
|
||||
type = QCborValue::Invalid;
|
||||
|
|
@ -1717,21 +1725,22 @@ QCborValue::QCborValue(const QCborMap &m)
|
|||
}
|
||||
|
||||
/*!
|
||||
\fn QCborValue::QCborValue(QCborTag t, const QCborValue &tv)
|
||||
\fn QCborValue::QCborValue(QCborKnownTags t, const QCborValue &tv)
|
||||
\fn QCborValue::QCborValue(QCborTag tag, const QCborValue &tv)
|
||||
\fn QCborValue::QCborValue(QCborKnownTags tag, const QCborValue &tv)
|
||||
|
||||
Creates a QCborValue for the extended type represented by the tag value \a
|
||||
t, tagging value \a tv. The tag can later be retrieved using tag() and
|
||||
tag, tagging value \a tv. The tag can later be retrieved using tag() and
|
||||
the tagged value using taggedValue().
|
||||
|
||||
\sa isTag(), tag(), taggedValue(), QCborKnownTags
|
||||
*/
|
||||
QCborValue::QCborValue(QCborTag t, const QCborValue &tv)
|
||||
QCborValue::QCborValue(QCborTag tag, const QCborValue &tv)
|
||||
: n(-1), container(new QCborContainerPrivate), t(Tag)
|
||||
{
|
||||
container->ref.storeRelaxed(1);
|
||||
container->append(t);
|
||||
container->append(tag);
|
||||
container->append(tv);
|
||||
t = convertToExtendedType(container);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include <QtCore/qcborvalue.h>
|
||||
#include <QtTest>
|
||||
|
||||
Q_DECLARE_METATYPE(QCborKnownTags)
|
||||
Q_DECLARE_METATYPE(QCborValue)
|
||||
Q_DECLARE_METATYPE(QCborValue::EncodingOptions)
|
||||
|
||||
|
|
@ -294,34 +295,77 @@ void tst_QCborValue::tagged()
|
|||
void tst_QCborValue::extendedTypes_data()
|
||||
{
|
||||
QTest::addColumn<QCborValue>("extended");
|
||||
QTest::addColumn<QCborValue>("tagged");
|
||||
QTest::addColumn<QCborKnownTags>("tag");
|
||||
QTest::addColumn<QCborValue>("taggedValue");
|
||||
QTest::addColumn<QCborValue>("correctedTaggedValue");
|
||||
QCborValue v(QCborValue::Invalid);
|
||||
QDateTime dt = QDateTime::currentDateTimeUtc();
|
||||
QDateTime dtTzOffset(dt.date(), dt.time(), Qt::OffsetFromUTC, dt.offsetFromUtc());
|
||||
QUuid uuid = QUuid::createUuid();
|
||||
|
||||
// non-correcting extended types (tagged value remains unchanged)
|
||||
QTest::newRow("DateTime") << QCborValue(dt)
|
||||
<< QCborValue(QCborKnownTags::DateTimeString, dt.toString(Qt::ISODateWithMs));
|
||||
<< QCborKnownTags::DateTimeString << QCborValue(dt.toString(Qt::ISODateWithMs)) << v;
|
||||
QTest::newRow("DateTime:TzOffset") << QCborValue(dtTzOffset)
|
||||
<< QCborKnownTags::DateTimeString << QCborValue(dtTzOffset.toString(Qt::ISODateWithMs)) << v;
|
||||
QTest::newRow("Url:Empty") << QCborValue(QUrl())
|
||||
<< QCborValue(QCborKnownTags::Url, QString());
|
||||
<< QCborKnownTags::Url << QCborValue(QString()) << v;
|
||||
QTest::newRow("Url:Authority") << QCborValue(QUrl("https://example.com"))
|
||||
<< QCborValue(QCborKnownTags::Url, "https://example.com");
|
||||
<< QCborKnownTags::Url << QCborValue("https://example.com") << v;
|
||||
QTest::newRow("Url:Path") << QCborValue(QUrl("file:///tmp/none"))
|
||||
<< QCborValue(QCborKnownTags::Url, "file:///tmp/none");
|
||||
<< QCborKnownTags::Url << QCborValue("file:///tmp/none") << v;
|
||||
QTest::newRow("Url:QueryFragment") << QCborValue(QUrl("whatever:?a=b&c=d#e"))
|
||||
<< QCborValue(QCborKnownTags::Url, "whatever:?a=b&c=d#e");
|
||||
<< QCborKnownTags::Url << QCborValue("whatever:?a=b&c=d#e") << v;
|
||||
QTest::newRow("Regex:Empty") << QCborValue(QRegularExpression())
|
||||
<< QCborValue(QCborKnownTags::RegularExpression, QString());
|
||||
<< QCborKnownTags::RegularExpression << QCborValue(QString()) << v;
|
||||
QTest::newRow("Regex") << QCborValue(QRegularExpression("^.*$"))
|
||||
<< QCborValue(QCborKnownTags::RegularExpression, QString("^.*$"));
|
||||
<< QCborKnownTags::RegularExpression << QCborValue(QString("^.*$")) << v;
|
||||
QTest::newRow("Uuid") << QCborValue(uuid)
|
||||
<< QCborValue(QCborKnownTags::Uuid, uuid.toRfc4122());
|
||||
<< QCborKnownTags::Uuid << QCborValue(uuid.toRfc4122()) << v;
|
||||
|
||||
// correcting extended types
|
||||
QDateTime dtNoMsecs = dt.fromSecsSinceEpoch(dt.toSecsSinceEpoch(), Qt::UTC);
|
||||
QUrl url("https://example.com/\xc2\xa9 ");
|
||||
QTest::newRow("UnixTime_t:Integer") << QCborValue(dtNoMsecs) << QCborKnownTags::UnixTime_t
|
||||
<< QCborValue(dtNoMsecs.toSecsSinceEpoch())
|
||||
<< QCborValue(dtNoMsecs.toString(Qt::ISODateWithMs));
|
||||
QTest::newRow("UnixTime_t:Double") << QCborValue(dt) << QCborKnownTags::UnixTime_t
|
||||
<< QCborValue(dt.toMSecsSinceEpoch() / 1000.)
|
||||
<< QCborValue(dt.toString(Qt::ISODateWithMs));
|
||||
QTest::newRow("DateTime::JustDate") << QCborValue(QDateTime({2018, 1, 1}, {}))
|
||||
<< QCborKnownTags::DateTimeString
|
||||
<< QCborValue("2018-01-01") << QCborValue("2018-01-01T00:00:00.000");
|
||||
QTest::newRow("DateTime::TzOffset") << QCborValue(QDateTime({2018, 1, 1}, {9, 0, 0}, Qt::UTC))
|
||||
<< QCborKnownTags::DateTimeString
|
||||
<< QCborValue("2018-01-01T09:00:00.000+00:00")
|
||||
<< QCborValue("2018-01-01T09:00:00.000Z");
|
||||
QTest::newRow("Url:NotNormalized") << QCborValue(url) << QCborKnownTags::Url
|
||||
<< QCborValue("HTTPS://EXAMPLE.COM/%c2%a9%20")
|
||||
<< QCborValue(url.toString());
|
||||
QTest::newRow("Uuid:Zero") << QCborValue(QUuid()) << QCborKnownTags::Uuid
|
||||
<< QCborValue(QByteArray())
|
||||
<< QCborValue(QByteArray(sizeof(QUuid), 0));
|
||||
QTest::newRow("Uuid:TooShort") << QCborValue(QUuid(0x12345678, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
|
||||
<< QCborKnownTags::Uuid
|
||||
<< QCborValue(raw("\x12\x34\x56\x78"))
|
||||
<< QCborValue(raw("\x12\x34\x56\x78" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0"));
|
||||
QTest::newRow("Uuid:TooLong") << QCborValue(uuid) << QCborKnownTags::Uuid
|
||||
<< QCborValue(uuid.toRfc4122() + "\1\2\3\4") << QCborValue(uuid.toRfc4122());
|
||||
}
|
||||
|
||||
void tst_QCborValue::extendedTypes()
|
||||
{
|
||||
QFETCH(QCborValue, extended);
|
||||
QFETCH(QCborValue, tagged);
|
||||
QFETCH(QCborKnownTags, tag);
|
||||
QFETCH(QCborValue, taggedValue);
|
||||
QFETCH(QCborValue, correctedTaggedValue);
|
||||
if (correctedTaggedValue.isInvalid())
|
||||
correctedTaggedValue = taggedValue;
|
||||
|
||||
QCborValue tagged(tag, taggedValue);
|
||||
QVERIFY(extended.isTag());
|
||||
QVERIFY(tagged.isTag());
|
||||
QCOMPARE(tagged.taggedValue(), correctedTaggedValue);
|
||||
QVERIFY(extended == tagged);
|
||||
QVERIFY(tagged == extended);
|
||||
|
||||
|
|
@ -1224,8 +1268,11 @@ void tst_QCborValue::sorting()
|
|||
QCborValue va1(QCborValue::Array), va2(QCborArray{1}), va3(QCborArray{0, 0});
|
||||
QCborValue vm1(QCborValue::Map), vm2(QCborMap{{1, 0}}), vm3(QCborMap{{0, 0}, {1, 0}});
|
||||
QCborValue vdt1(QDateTime::fromMSecsSinceEpoch(0, Qt::UTC)), vdt2(QDateTime::currentDateTimeUtc());
|
||||
QCborValue vtagged1(QCborKnownTags::UnixTime_t, 0), vtagged2(QCborKnownTags::UnixTime_t, 0.0),
|
||||
vtagged3(QCborKnownTags::Signature, 0), vtagged4(QCborTag(-2), 0), vtagged5(QCborTag(-1), 0);
|
||||
QCborValue vtagged1(QCborKnownTags::PositiveBignum, QByteArray()),
|
||||
vtagged2(QCborKnownTags::PositiveBignum, 0.0), // bignums are supposed to have byte arrays...
|
||||
vtagged3(QCborKnownTags::Signature, 0),
|
||||
vtagged4(QCborTag(-2), 0),
|
||||
vtagged5(QCborTag(-1), 0);
|
||||
QCborValue vurl1(QUrl("https://example.net")), vurl2(QUrl("https://example.com/"));
|
||||
QCborValue vuuid1{QUuid()}, vuuid2(QUuid::createUuid());
|
||||
QCborValue vsimple1(QCborSimpleType(1)), vsimple32(QCborSimpleType(32)), vsimple255(QCborSimpleType(255));
|
||||
|
|
|
|||
Loading…
Reference in New Issue