Long live QCborStreamWriter!

CBOR is the Concise Binary Object Representation, a very compact form of
binary data encoding that is compatible with JSON. It was created by the
IETF Constrained RESTful Environments (CoRE) WG[1] and has since been
used in many new RFCs by that group, especially COSE[2], and is meant to
be used alongside CoAP[3].

This API is a very simple, thin wrapper around TinyCBOR[4].

See RFC 7049 <http://tools.ietf.org/html/rfc7049>.

[1] https://datatracker.ietf.org/wg/core/charter/
[2] https://tools.ietf.org/html/rfc8152
[3] https://tools.ietf.org/html/rfc7252
[4] https://github.com/intel/tinycbor

Change-Id: Ia0aac2f09e9245339951ffff13c651cfeab77d3b
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
bb10
Thiago Macieira 2015-02-25 18:04:00 -08:00
parent 0619055430
commit e3cb38b06a
7 changed files with 1819 additions and 0 deletions

View File

@ -0,0 +1,127 @@
/****************************************************************************
**
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QCBORCOMMON_H
#define QCBORCOMMON_H
#include <QtCore/qobjectdefs.h>
#include <QtCore/qmetatype.h>
#if 0
#pragma qt_class(QtCborCommon)
#endif
QT_BEGIN_NAMESPACE
enum class QCborSimpleType : quint8 {
False = 20,
True = 21,
Null = 22,
Undefined = 23
};
enum class QCborTag : quint64 {};
enum class QCborKnownTags {
DateTimeString = 0,
UnixTime_t = 1,
PositiveBignum = 2,
NegativeBignum = 3,
Decimal = 4,
Bigfloat = 5,
COSE_Encrypt0 = 16,
COSE_Mac0 = 17,
COSE_Sign1 = 18,
ExpectedBase64url = 21,
ExpectedBase64 = 22,
ExpectedBase16 = 23,
EncodedCbor = 24,
Url = 32,
Base64url = 33,
Base64 = 34,
RegularExpression = 35,
MimeMessage = 36,
Uuid = 37,
COSE_Encrypt = 96,
COSE_Mac = 97,
COSE_Sign = 98,
Signature = 55799
};
inline bool operator==(QCborTag t, QCborKnownTags kt) { return quint64(t) == quint64(kt); }
inline bool operator==(QCborKnownTags kt, QCborTag t) { return quint64(t) == quint64(kt); }
inline bool operator!=(QCborTag t, QCborKnownTags kt) { return quint64(t) != quint64(kt); }
inline bool operator!=(QCborKnownTags kt, QCborTag t) { return quint64(t) != quint64(kt); }
struct Q_CORE_EXPORT QCborError
{
Q_GADGET
public:
enum Code : int {
UnknownError = 1,
AdvancePastEnd = 3,
InputOutputError = 4,
GarbageAtEnd = 256,
EndOfFile,
UnexpectedBreak,
UnknownType,
IllegalType,
IllegalNumber,
IllegalSimpleType,
InvalidUtf8String = 516,
DataTooLarge = 1024,
NestingTooDeep,
UnsupportedType,
NoError = 0
};
Q_ENUM(Code)
Code c;
operator Code() const { return c; }
QString toString() const;
};
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QCborSimpleType)
Q_DECLARE_METATYPE(QCborTag)
#endif // QCBORSTREAM_H

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,115 @@
/****************************************************************************
**
** Copyright (C) 2018 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QCBORSTREAM_H
#define QCBORSTREAM_H
#include <QtCore/qbytearray.h>
#include <QtCore/qcborcommon.h>
#include <QtCore/qfloat16.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringview.h>
QT_BEGIN_NAMESPACE
class QIODevice;
enum class QCborNegativeInteger : quint64 {};
class QCborStreamWriterPrivate;
class Q_CORE_EXPORT QCborStreamWriter
{
public:
explicit QCborStreamWriter(QIODevice *device);
explicit QCborStreamWriter(QByteArray *data);
~QCborStreamWriter();
Q_DISABLE_COPY(QCborStreamWriter)
void setDevice(QIODevice *device);
QIODevice *device() const;
void append(quint64 u);
void append(qint64 i);
void append(QCborNegativeInteger n);
void append(const QByteArray &ba) { appendByteString(ba.constData(), ba.size()); }
void append(QLatin1String str);
void append(QStringView str);
void append(QCborTag tag);
void append(QCborKnownTags tag) { append(QCborTag(tag)); }
void append(QCborSimpleType st);
void append(std::nullptr_t) { append(QCborSimpleType::Null); }
void append(qfloat16 f);
void append(float f);
void append(double d);
void appendByteString(const char *data, qsizetype len);
void appendTextString(const char *utf8, qsizetype len);
// convenience
void append(bool b) { append(b ? QCborSimpleType::True : QCborSimpleType::False); }
void appendNull() { append(QCborSimpleType::Null); }
void appendUndefined() { append(QCborSimpleType::Undefined); }
#ifndef Q_QDOC
// overloads to make normal code not complain
void append(int i) { append(qint64(i)); }
void append(uint u) { append(quint64(u)); }
#endif
#ifndef QT_NO_CAST_FROM_ASCII
void append(const char *str, qsizetype size = -1)
{ appendTextString(str, (str && size == -1) ? int(strlen(str)) : size); }
#endif
void startArray();
void startArray(quint64 count);
bool endArray();
void startMap();
void startMap(quint64 count);
bool endMap();
// no API for encoding chunked strings
private:
QScopedPointer<QCborStreamWriterPrivate> d;
};
QT_END_NAMESPACE
#endif // QCBORSTREAM_H

View File

@ -1,6 +1,8 @@
# Qt data formats core module
HEADERS += \
serialization/qcborcommon.h \
serialization/qcborstream.h \
serialization/qdatastream.h \
serialization/qdatastream_p.h \
serialization/qjson_p.h \
@ -17,6 +19,7 @@ HEADERS += \
serialization/qxmlutils_p.h
SOURCES += \
serialization/qcborstream.cpp \
serialization/qdatastream.cpp \
serialization/qjson.cpp \
serialization/qjsondocument.cpp \
@ -28,3 +31,5 @@ SOURCES += \
serialization/qtextstream.cpp \
serialization/qxmlstream.cpp \
serialization/qxmlutils.cpp
INCLUDEPATH += ../3rdparty/tinycbor/src

View File

@ -0,0 +1,8 @@
QT = core testlib
TARGET = tst_qcborstreamwriter
CONFIG += testcase
SOURCES += \
tst_qcborstreamwriter.cpp
INCLUDEPATH += ../../../../../src/3rdparty/tinycbor/tests/encoder
DEFINES += SRCDIR=\\\"$$PWD/\\\"

View File

@ -0,0 +1,313 @@
/****************************************************************************
**
** 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 <QtTest>
class tst_QCborStreamWriter : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase_data();
void fixed_data();
void fixed();
void strings_data();
void strings() { fixed(); }
void nonAsciiStrings_data();
void nonAsciiStrings();
void arraysAndMaps_data();
void arraysAndMaps() { fixed(); }
void tags_data();
void tags();
void arrays_data() { tags_data(); }
void arrays();
void maps_data() { tags_data(); }
void maps();
};
// Get the data from TinyCBOR (see src/3rdparty/tinycbor/tests/encoder/data.cpp)
typedef quint64 CborTag;
#include "data.cpp"
void encodeVariant(QCborStreamWriter &writer, const QVariant &v)
{
int type = v.userType();
switch (type) {
case QVariant::Int:
case QVariant::LongLong:
return writer.append(v.toLongLong());
case QVariant::UInt:
case QVariant::ULongLong:
return writer.append(v.toULongLong());
case QVariant::Bool:
return writer.append(v.toBool());
case QVariant::Invalid:
return writer.appendUndefined();
case QMetaType::VoidStar:
return writer.append(nullptr);
case QVariant::Double:
return writer.append(v.toDouble());
case QMetaType::Float:
return writer.append(v.toFloat());
case QVariant::String:
return writer.append(v.toString());
case QVariant::ByteArray:
return writer.append(v.toByteArray());
default:
if (type == qMetaTypeId<NegativeInteger>())
return writer.append(QCborNegativeInteger(v.value<NegativeInteger>().abs));
if (type == qMetaTypeId<SimpleType>())
return writer.append(QCborSimpleType(v.value<SimpleType>().type));
if (type == qMetaTypeId<qfloat16>())
return writer.append(v.value<qfloat16>());
if (type == qMetaTypeId<Tag>()) {
writer.append(QCborTag(v.value<Tag>().tag));
return encodeVariant(writer, v.value<Tag>().tagged);
}
if (type == QVariant::List || type == qMetaTypeId<IndeterminateLengthArray>()) {
QVariantList list = v.toList();
if (type == qMetaTypeId<IndeterminateLengthArray>()) {
list = v.value<IndeterminateLengthArray>();
writer.startArray();
} else {
writer.startArray(list.length());
}
for (const QVariant &v2 : qAsConst(list))
encodeVariant(writer, v2);
QVERIFY(writer.endArray());
return;
}
if (type == qMetaTypeId<Map>() || type == qMetaTypeId<IndeterminateLengthMap>()) {
Map map = v.value<Map>();
if (type == qMetaTypeId<IndeterminateLengthMap>()) {
map = v.value<IndeterminateLengthMap>();
writer.startMap();
} else {
writer.startMap(map.length());
}
for (auto pair : qAsConst(map)) {
encodeVariant(writer, pair.first);
encodeVariant(writer, pair.second);
}
QVERIFY(writer.endMap());
return;
}
}
QFAIL("Shouldn't have got here");
}
void compare(const QVariant &input, const QByteArray &output)
{
QFETCH_GLOBAL(bool, useDevice);
if (useDevice) {
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
QCborStreamWriter writer(&buffer);
encodeVariant(writer, input);
QCOMPARE(buffer.data(), output);
} else {
QByteArray buffer;
QCborStreamWriter writer(&buffer);
encodeVariant(writer, input);
QCOMPARE(buffer, output);
}
}
void tst_QCborStreamWriter::initTestCase_data()
{
QTest::addColumn<bool>("useDevice");
QTest::newRow("QByteArray") << false;
QTest::newRow("QIODevice") << true;
}
void tst_QCborStreamWriter::fixed_data()
{
addColumns();
addFixedData();
}
void tst_QCborStreamWriter::fixed()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(input, output);
}
void tst_QCborStreamWriter::strings_data()
{
addColumns();
addStringsData();
}
void tst_QCborStreamWriter::nonAsciiStrings_data()
{
QTest::addColumn<QByteArray>("output");
QTest::addColumn<QString>("input");
QTest::addColumn<bool>("isLatin1");
QByteArray latin1 = u8"Résumé";
QTest::newRow("shortlatin1")
<< ("\x68" + latin1) << QString::fromUtf8(latin1) << true;
// replicate it 5 times (total 40 bytes)
latin1 += latin1 + latin1 + latin1 + latin1;
QTest::newRow("longlatin1")
<< ("\x78\x28" + latin1) << QString::fromUtf8(latin1) << true;
QByteArray nonlatin1 = u8"Χαίρετε";
QTest::newRow("shortnonlatin1")
<< ("\x6e" + nonlatin1) << QString::fromUtf8(nonlatin1) << false;
// replicate it 4 times (total 56 bytes)
nonlatin1 = nonlatin1 + nonlatin1 + nonlatin1 + nonlatin1;
QTest::newRow("longnonlatin1")
<< ("\x78\x38" + nonlatin1) << QString::fromUtf8(nonlatin1) << false;
}
void tst_QCborStreamWriter::nonAsciiStrings()
{
QFETCH(QByteArray, output);
QFETCH(QString, input);
QFETCH(bool, isLatin1);
QFETCH_GLOBAL(bool, useDevice);
// will be wrong if !isLatin1
QByteArray latin1 = input.toLatin1();
if (useDevice) {
{
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
QCborStreamWriter writer(&buffer);
writer.append(input);
QCOMPARE(buffer.data(), output);
}
if (isLatin1) {
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
QCborStreamWriter writer(&buffer);
writer.append(QLatin1String(latin1.constData(), latin1.size()));
QCOMPARE(buffer.data(), output);
}
} else {
{
QByteArray buffer;
QCborStreamWriter writer(&buffer);
encodeVariant(writer, input);
QCOMPARE(buffer, output);
}
if (isLatin1) {
QByteArray buffer;
QCborStreamWriter writer(&buffer);
writer.append(QLatin1String(latin1.constData(), latin1.size()));
QCOMPARE(buffer, output);
}
}
}
void tst_QCborStreamWriter::arraysAndMaps_data()
{
addColumns();
addArraysAndMaps();
}
void tst_QCborStreamWriter::tags_data()
{
addColumns();
addFixedData();
addStringsData();
addArraysAndMaps();
}
void tst_QCborStreamWriter::tags()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(QVariant::fromValue(Tag{1, input}), "\xc1" + output);
}
void tst_QCborStreamWriter::arrays()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(make_list(input), "\x81" + output);
if (QTest::currentTestFailed())
return;
compare(make_list(input, input), "\x82" + output + output);
if (QTest::currentTestFailed())
return;
// nested lists
compare(make_list(make_list(input)), "\x81\x81" + output);
if (QTest::currentTestFailed())
return;
compare(make_list(make_list(input), make_list(input)), "\x82\x81" + output + "\x81" + output);
}
void tst_QCborStreamWriter::maps()
{
QFETCH(QVariant, input);
QFETCH(QByteArray, output);
compare(make_map({{1, input}}), "\xa1\1" + output);
if (QTest::currentTestFailed())
return;
compare(make_map({{1, input}, {input, 24}}), "\xa2\1" + output + output + "\x18\x18");
}
QTEST_MAIN(tst_QCborStreamWriter)
#include "tst_qcborstreamwriter.moc"

View File

@ -1,6 +1,7 @@
TEMPLATE = subdirs
SUBDIRS = \
json \
qcborstreamwriter \
qdatastream \
qtextstream \
qxmlstream