diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index da0fccb834..325a8548cf 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -24,7 +24,8 @@ QOBJS = \ qfile.o qfiledevice.o qfileinfo.o qfilesystemengine.o \ qfilesystementry.o qfsfileengine.o qfsfileengine_iterator.o \ qiodevice.o qsettings.o qtemporaryfile.o qtextstream.o \ - qcborvalue.o qjsoncbor.o qjsonarray.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \ + qcborstreamwriter.o qcborvalue.o \ + qjsoncbor.o qjsonarray.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \ qmetatype.o qsystemerror.o qvariant.o \ quuid.o \ qarraydata.o qbitarray.o qbytearray.o qbytearraylist.o qbytearraymatcher.o \ @@ -96,6 +97,7 @@ DEPEND_SRC = \ $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp \ $(SOURCE_PATH)/src/corelib/plugin/quuid.cpp \ + $(SOURCE_PATH)/src/corelib/serialization/qcborstreamwriter.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qcborvalue.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp \ $(SOURCE_PATH)/src/corelib/serialization/qjsonarray.cpp \ @@ -136,6 +138,7 @@ DEPEND_SRC = \ CPPFLAGS = -g $(EXTRA_CPPFLAGS) \ -I$(QMKSRC) -I$(QMKLIBSRC) -I$(QMKGENSRC) \ + -I$(SOURCE_PATH)/src/3rdparty/tinycbor/src \ -I$(QMKGENSRC)/unix -I$(QMKGENSRC)/win32 -I$(QMKGENSRC)/mac \ -I$(INC_PATH) -I$(INC_PATH)/QtCore \ -I$(INC_PATH)/QtCore/$(QT_VERSION) -I$(INC_PATH)/QtCore/$(QT_VERSION)/QtCore \ @@ -467,6 +470,9 @@ qsystemlibrary.o: $(SOURCE_PATH)/src/corelib/plugin/qsystemlibrary.cpp qdatastream.o: $(SOURCE_PATH)/src/corelib/serialization/qdatastream.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< +qcborstreamwriter.o: $(SOURCE_PATH)/src/corelib/serialization/qcborstreamwriter.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $< + qcborvalue.o: $(SOURCE_PATH)/src/corelib/serialization/qcborvalue.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< diff --git a/qmake/Makefile.win32 b/qmake/Makefile.win32 index ab49113fc7..6c8d5ec922 100644 --- a/qmake/Makefile.win32 +++ b/qmake/Makefile.win32 @@ -31,6 +31,7 @@ CFLAGS_BARE = -c -Fo./ -Fdqmake.pdb \ -W2 -nologo -O2 \ $(CFLAGS_EXTRA) \ -I$(QMKSRC) -I$(QMKSRC)\library -I$(QMKSRC)\generators -I$(QMKSRC)\generators\unix -I$(QMKSRC)\generators\win32 -I$(QMKSRC)\generators\mac \ + -I$(SOURCE_PATH)/src/3rdparty/tinycbor/src \ -I$(INC_PATH) -I$(INC_PATH)\QtCore -I$(INC_PATH)\QtCore\$(QT_VERSION) -I$(INC_PATH)\QtCore\$(QT_VERSION)\QtCore \ -I$(BUILD_PATH)\src\corelib\global \ -I$(SOURCE_PATH)\mkspecs\$(QMAKESPEC) \ @@ -118,6 +119,7 @@ QTOBJS= \ qxmlutils.obj \ qnumeric.obj \ qlogging.obj \ + qcborstreamwriter.obj \ qcborvalue.obj \ qjsoncbor.obj \ qjsondocument.obj \ diff --git a/qmake/qmake.pro b/qmake/qmake.pro index 42c727b33e..745ee9b6af 100644 --- a/qmake/qmake.pro +++ b/qmake/qmake.pro @@ -31,7 +31,8 @@ INCLUDEPATH += \ generators \ generators/unix \ generators/win32 \ - generators/mac + generators/mac \ + ../src/3rdparty/tinycbor/src SOURCES += \ main.cpp \ @@ -116,6 +117,7 @@ SOURCES += \ qbytearray.cpp \ qbytearraymatcher.cpp \ qcalendar.cpp \ + qcborstreamwriter.cpp \ qcborvalue.cpp \ qcryptographichash.cpp \ qdatetime.cpp \ @@ -175,6 +177,7 @@ HEADERS += \ qcalendar.h \ qcalendarbackend_p.h \ qcalendarmath_p.h \ + qcborstreamwriter.h \ qcborvalue.h \ qcborvalue_p.h \ qchar.h \ diff --git a/src/corelib/configure.json b/src/corelib/configure.json index 04643aec33..eb60ad213c 100644 --- a/src/corelib/configure.json +++ b/src/corelib/configure.json @@ -1087,14 +1087,20 @@ Mozilla License) is included. The data is then also used in QNetworkCookieJar::v "label": "Windows System Libraries", "condition": "config.win32 && libs.advapi32 && libs.gdi32 && libs.kernel32 && libs.netapi32 && libs.ole32 && libs.shell32 && libs.uuid && libs.user32 && libs.winmm && libs.ws2_32" }, - "cborstream": { - "label": "CBOR stream I/O", - "purpose": "Provides support for reading and writing the CBOR binary format. + "cborstreamreader": { + "label": "CBOR stream reading", + "purpose": "Provides support for reading the CBOR binary format. Note that this is required for plugin loading. Qt GUI needs QPA plugins for basic operation.", "section": "Utilities", "output": [ "publicFeature" ] }, + "cborstreamwriter": { + "label": "CBOR stream writing", + "purpose": "Provides support for writing the CBOR binary format.", + "section": "Utilities", + "output": [ "publicFeature" ] + }, "binaryjson": { "label": "Binary JSON (deprecated)", "purpose": "Provides support for the deprecated binary JSON format.", diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h index b62c1a4d35..b3daf43c04 100644 --- a/src/corelib/global/qconfig-bootstrapped.h +++ b/src/corelib/global/qconfig-bootstrapped.h @@ -75,7 +75,8 @@ # define QT_FEATURE_alloca_malloc_h -1 #endif #define QT_FEATURE_binaryjson -1 -#define QT_FEATURE_cborstream -1 +#define QT_FEATURE_cborstreamreader -1 +#define QT_FEATURE_cborstreamwriter 1 #define QT_CRYPTOGRAPHICHASH_ONLY_SHA1 #define QT_FEATURE_cxx11_random (__has_include() ? 1 : -1) #define QT_NO_DATASTREAM diff --git a/src/corelib/serialization/qcborcommon.cpp b/src/corelib/serialization/qcborcommon.cpp new file mode 100644 index 0000000000..37fb198744 --- /dev/null +++ b/src/corelib/serialization/qcborcommon.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2019 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#define CBOR_NO_ENCODER_API +#define CBOR_NO_PARSER_API +#include "qcborcommon_p.h" + +#include + +QT_BEGIN_NAMESPACE + +#include + +/*! + \headerfile + + \brief The header contains definitions common to both the + streaming classes (QCborStreamReader and QCborStreamWriter) and to + QCborValue. + */ + +/*! + \enum QCborSimpleType + \relates + + This enum contains the possible "Simple Types" for CBOR. Simple Types range + from 0 to 255 and are types that carry no further value. + + The following values are currently known: + + \value False A "false" boolean. + \value True A "true" boolean. + \value Null Absence of value (null). + \value Undefined Missing or deleted value, usually an error. + + Qt CBOR API supports encoding and decoding any Simple Type, whether one of + those above or any other value. + + Applications should only use further values if a corresponding specification + has been published, otherwise interpretation and validation by the remote + may fail. Values 24 to 31 are reserved and must not be used. + + The current authoritative list is maintained by IANA in the + \l{https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xml}{Simple + Values registry}. + + \sa QCborStreamWriter::append(QCborSimpleType), QCborStreamReader::isSimpleType(), + QCborStreamReader::toSimpleType(), QCborValue::isSimpleType(), QCborValue::toSimpleType() + */ + +#if !defined(QT_NO_DATASTREAM) +QDataStream &operator<<(QDataStream &ds, QCborSimpleType st) +{ + return ds << quint8(st); +} + +QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st) +{ + quint8 v; + ds >> v; + st = QCborSimpleType(v); + return ds; +} +#endif + +/*! + \enum QCborTag + \relates + + This enum contains no enumeration and is used only to provide type-safe + access to a CBOR tag. + + CBOR tags are 64-bit numbers that are attached to generic CBOR types to + provide further semantic meaning. QCborTag may be constructed from an + enumeration found in QCborKnownTags or directly by providing the numeric + representation. + + For example, the following creates a QCborValue containing a byte array + tagged with a tag 2. + + \snippet code/src_corelib_serialization_qcborstream.cpp 0 + + \sa QCborKnownTags, QCborStreamWriter::append(QCborTag), + QCborStreamReader::isTag(), QCborStreamReader::toTag(), + QCborValue::isTag(), QCborValue::tag() + */ + +/*! + \enum QCborKnownTags + \relates + + This enum contains a list of CBOR tags, known at the time of the Qt + implementation. This list is not meant to be complete and contains only + tags that are either backed by an RFC or specifically used by the Qt + implementation. + + The authoritative list is maintained by IANA in the + \l{https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml}{CBOR tag + registry}. + + \value DateTimeString A date and time string, formatted according to RFC 3339, as refined + by RFC 4287. It is the same format as Qt::ISODate and + Qt::ISODateWithMs. + \value UnixTime_t A numerical representation of seconds elapsed since + 1970-01-01T00:00Z. + \value PositiveBignum A positive number of arbitrary length, encoded as a byte array in + network byte order. For example, the number 2\sup{64} is represented by + a byte array containing the byte value 0x01 followed by 8 zero bytes. + \value NegativeBignum A negative number of arbirary length, encoded as the absolute value + of that number, minus one. For example, a byte array containing + byte value 0x02 followed by 8 zero bytes represents the number + -2\sup{65} - 1. + \value Decimal A decimal fraction, encoded as an array of two integers: the first + is the exponent of the power of 10, the second the integral + mantissa. The value 273.15 would be encoded as array \c{[-2, 27315]}. + \value Bigfloat Similar to Decimal, but the exponent is a power of 2 instead. + \value COSE_Encrypt0 An \c Encrypt0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} + (CBOR Object Signing and Encryption). + \value COSE_Mac0 A \c Mac0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} + (CBOR Object Signing and Encryption). + \value COSE_Sign1 A \c Sign1 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} + (CBOR Object Signing and Encryption). + \value ExpectedBase64url Indicates that the byte array should be encoded using Base64url + if the stream is converted to JSON. + \value ExpectedBase64 Indicates that the byte array should be encoded using Base64 + if the stream is converted to JSON. + \value ExpectedBase16 Indicates that the byte array should be encoded using Base16 (hex) + if the stream is converted to JSON. + \value EncodedCbor Indicates that the byte array contains a CBOR stream. + \value Url Indicates that the string contains a URL. + \value Base64url Indicates that the string contains data encoded using Base64url. + \value Base64 Indicates that the string contains data encoded using Base64. + \value RegularExpression Indicates that the string contains a Perl-Compatible Regular + Expression pattern. + \value MimeMessage Indicates that the string contains a MIME message (according to + \l{https://tools.ietf.org/html/rfc2045}){RFC 2045}. + \value Uuid Indicates that the byte array contains a UUID. + \value COSE_Encrypt An \c Encrypt map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} + (CBOR Object Signing and Encryption). + \value COSE_Mac A \c Mac map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} + (CBOR Object Signing and Encryption). + \value COSE_Sign A \c Sign map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} + (CBOR Object Signing and Encryption). + \value Signature No change in interpretation; this tag can be used as the outermost + tag in a CBOR stream as the file header. + + The following tags are interpreted by QCborValue during decoding and will + produce objects with extended Qt types, and it will use those tags when + encoding the same extended types. + + \value DateTimeString \l QDateTime + \value UnixTime_t \l QDateTime (only in decoding) + \value Url \l QUrl + \value Uuid \l QUuid + + Additionally, if a QCborValue containing a QByteArray is tagged using one of + \c ExpectedBase64url, \c ExpectedBase64 or \c ExpectedBase16, QCborValue + will use the expected encoding when converting to JSON (see + QCborValue::toJsonValue). + + \sa QCborTag, QCborStreamWriter::append(QCborTag), + QCborStreamReader::isTag(), QCborStreamReader::toTag(), + QCborValue::isTag(), QCborValue::tag() + */ + +/*! + \class QCborError + \inmodule QtCore + \relates + \reentrant + \since 5.12 + + \brief The QCborError class holds the error condition found while parsing or + validating a CBOR stream. + + \sa QCborStreamReader, QCborValue, QCborParserError + */ + +/*! + \enum QCborError::Code + + This enum contains the possible error condition codes. + + \value NoError No error was detected. + \value UnknownError An unknown error occurred and no further details are available. + \value AdvancePastEnd QCborStreamReader::next() was called but there are no more elements in + the current context. + \value InputOutputError An I/O error with the QIODevice occurred. + \value GarbageAtEnd Data was found in the input stream after the last element. + \value EndOfFile The end of the input stream was unexpectedly reached while processing an + element. + \value UnexpectedBreak The CBOR stream contains a Break where it is not allowed (data is + corrupt and the error is not recoverable). + \value UnknownType The CBOR stream contains an unknown/unparseable Type (data is corrupt + and the and the error is not recoverable). + \value IllegalType The CBOR stream contains a known type in a position it is not allowed + to exist (data is corrupt and the error is not recoverable). + \value IllegalNumber The CBOR stream appears to be encoding a number larger than 64-bit + (data is corrupt and the error is not recoverable). + \value IllegalSimpleType The CBOR stream contains a Simple Type encoded incorrectly (data is + corrupt and the error is not recoverable). + \value InvalidUtf8String The CBOR stream contains a text string that does not decode properly + as UTF-8 (data is corrupt and the error is not recoverable). + \value DataTooLarge CBOR string, map or array is too big and cannot be parsed by Qt + (internal limitation, but the error is not recoverable). + \value NestingTooDeep Too many levels of arrays or maps encountered while processing the + input (internal limitation, but the error is not recoverable). + \value UnsupportedType The CBOR stream contains a known type that the implementation does not + support (internal limitation, but the error is not recoverable). + */ + +/*! + \variable QCborError::c + \internal + */ + +/*! + \fn QCborError::operator Code() const + + Returns the error code that this QCborError object stores. + */ + +/*! + Returns a text string that matches the error code in this QCborError object. + + Note: the string is not translated. Applications whose interface allow users + to parse CBOR streams need to provide their own, translated strings. + + \sa QCborError::Code + */ +QString QCborError::toString() const +{ + switch (c) { + case NoError: + Q_STATIC_ASSERT(int(NoError) == int(CborNoError)); + return QString(); + + case UnknownError: + Q_STATIC_ASSERT(int(UnknownError) == int(CborUnknownError)); + return QStringLiteral("Unknown error"); + case AdvancePastEnd: + Q_STATIC_ASSERT(int(AdvancePastEnd) == int(CborErrorAdvancePastEOF)); + return QStringLiteral("Read past end of buffer (more bytes needed)"); + case InputOutputError: + Q_STATIC_ASSERT(int(InputOutputError) == int(CborErrorIO)); + return QStringLiteral("Input/Output error"); + case GarbageAtEnd: + Q_STATIC_ASSERT(int(GarbageAtEnd) == int(CborErrorGarbageAtEnd)); + return QStringLiteral("Data found after the end of the stream"); + case EndOfFile: + Q_STATIC_ASSERT(int(EndOfFile) == int(CborErrorUnexpectedEOF)); + return QStringLiteral("Unexpected end of input data (more bytes needed)"); + case UnexpectedBreak: + Q_STATIC_ASSERT(int(UnexpectedBreak) == int(CborErrorUnexpectedBreak)); + return QStringLiteral("Invalid CBOR stream: unexpected 'break' byte"); + case UnknownType: + Q_STATIC_ASSERT(int(UnknownType) == int(CborErrorUnknownType)); + return QStringLiteral("Invalid CBOR stream: unknown type"); + case IllegalType: + Q_STATIC_ASSERT(int(IllegalType) == int(CborErrorIllegalType)); + return QStringLiteral("Invalid CBOR stream: illegal type found"); + case IllegalNumber: + Q_STATIC_ASSERT(int(IllegalNumber) == int(CborErrorIllegalNumber)); + return QStringLiteral("Invalid CBOR stream: illegal number encoding (future extension)"); + case IllegalSimpleType: + Q_STATIC_ASSERT(int(IllegalSimpleType) == int(CborErrorIllegalSimpleType)); + return QStringLiteral("Invalid CBOR stream: illegal simple type"); + case InvalidUtf8String: + Q_STATIC_ASSERT(int(InvalidUtf8String) == int(CborErrorInvalidUtf8TextString)); + return QStringLiteral("Invalid CBOR stream: invalid UTF-8 text string"); + case DataTooLarge: + Q_STATIC_ASSERT(int(DataTooLarge) == int(CborErrorDataTooLarge)); + return QStringLiteral("Internal limitation: data set too large"); + case NestingTooDeep: + Q_STATIC_ASSERT(int(NestingTooDeep) == int(CborErrorNestingTooDeep)); + return QStringLiteral("Internal limitation: data nesting too deep"); + case UnsupportedType: + Q_STATIC_ASSERT(int(UnsupportedType) == int(CborErrorUnsupportedType)); + return QStringLiteral("Internal limitation: unsupported type"); + } + + // get the error string from TinyCBOR + CborError err = CborError(int(c)); + return QString::fromLatin1(cbor_error_string(err)); +} + +QT_END_NAMESPACE + +#ifndef QT_BOOTSTRAPPED +#include "moc_qcborcommon.cpp" +#endif diff --git a/src/corelib/serialization/qcborcommon.h b/src/corelib/serialization/qcborcommon.h index 3dfe50cd09..bec46399ce 100644 --- a/src/corelib/serialization/qcborcommon.h +++ b/src/corelib/serialization/qcborcommon.h @@ -148,6 +148,8 @@ inline uint qHash(QCborTag tag, uint seed = 0) return qHash(quint64(tag), seed); } +enum class QCborNegativeInteger : quint64 {}; + QT_END_NAMESPACE Q_DECLARE_METATYPE(QCborTag) diff --git a/src/corelib/serialization/qcborcommon_p.h b/src/corelib/serialization/qcborcommon_p.h new file mode 100644 index 0000000000..9b7f4b7099 --- /dev/null +++ b/src/corelib/serialization/qcborcommon_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation. +** Copyright (C) 2019 The Qt Company Ltd. +** 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_P_H +#define QCBORCOMMON_P_H + +#include "qcborcommon.h" + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +#ifdef QT_NO_DEBUG +# define NDEBUG 1 +#endif +#undef assert +#define assert Q_ASSERT + +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wunused-function") +QT_WARNING_DISABLE_CLANG("-Wunused-function") +QT_WARNING_DISABLE_CLANG("-Wundefined-internal") + +#define CBOR_NO_VALIDATION_API 1 +#define CBOR_NO_PRETTY_API 1 +#define CBOR_API static inline +#define CBOR_PRIVATE_API static inline +#define CBOR_INLINE_API static inline + +#include + +QT_WARNING_POP + +Q_DECLARE_TYPEINFO(CborValue, Q_PRIMITIVE_TYPE); + +QT_END_NAMESPACE + +#endif // QCBORCOMMON_P_H diff --git a/src/corelib/serialization/qcborstream.h b/src/corelib/serialization/qcborstream.h index 08bf680cca..f2b88820cd 100644 --- a/src/corelib/serialization/qcborstream.h +++ b/src/corelib/serialization/qcborstream.h @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2018 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -40,230 +41,17 @@ #ifndef QCBORSTREAM_H #define QCBORSTREAM_H -#include -#include -#include -#include -#include -#include +#include -QT_REQUIRE_CONFIG(cborstream); +#if QT_CONFIG(cborstreamreader) +#include +#endif -// See qcborcommon.h for why we check -#if defined(QT_X11_DEFINES_FOUND) -# undef True -# undef False +#if QT_CONFIG(cborstreamwriter) +#include #endif 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 d; -}; - -class QCborStreamReaderPrivate; -class Q_CORE_EXPORT QCborStreamReader -{ - Q_GADGET -public: - enum Type : quint8 { - UnsignedInteger = 0x00, - NegativeInteger = 0x20, - ByteString = 0x40, - ByteArray = ByteString, - TextString = 0x60, - String = TextString, - Array = 0x80, - Map = 0xa0, - Tag = 0xc0, - SimpleType = 0xe0, - HalfFloat = 0xf9, - Float16 = HalfFloat, - Float = 0xfa, - Double = 0xfb, - - Invalid = 0xff - }; - Q_ENUM(Type) - - enum StringResultCode { - EndOfString = 0, - Ok = 1, - Error = -1 - }; - template struct StringResult { - Container data; - StringResultCode status = Error; - }; - Q_ENUM(StringResultCode) - - QCborStreamReader(); - QCborStreamReader(const char *data, qsizetype len); - QCborStreamReader(const quint8 *data, qsizetype len); - explicit QCborStreamReader(const QByteArray &data); - explicit QCborStreamReader(QIODevice *device); - ~QCborStreamReader(); - Q_DISABLE_COPY(QCborStreamReader) - - void setDevice(QIODevice *device); - QIODevice *device() const; - void addData(const QByteArray &data); - void addData(const char *data, qsizetype len); - void addData(const quint8 *data, qsizetype len) - { addData(reinterpret_cast(data), len); } - void reparse(); - void clear(); - void reset(); - - QCborError lastError(); - - qint64 currentOffset() const; - - bool isValid() const { return !isInvalid(); } - - int containerDepth() const; - QCborStreamReader::Type parentContainerType() const; - bool hasNext() const noexcept Q_DECL_PURE_FUNCTION; - bool next(int maxRecursion = 10000); - - Type type() const { return QCborStreamReader::Type(type_); } - bool isUnsignedInteger() const { return type() == UnsignedInteger; } - bool isNegativeInteger() const { return type() == NegativeInteger; } - bool isInteger() const { return quint8(type()) <= quint8(NegativeInteger); } - bool isByteArray() const { return type() == ByteArray; } - bool isString() const { return type() == String; } - bool isArray() const { return type() == Array; } - bool isMap() const { return type() == Map; } - bool isTag() const { return type() == Tag; } - bool isSimpleType() const { return type() == SimpleType; } - bool isFloat16() const { return type() == Float16; } - bool isFloat() const { return type() == Float; } - bool isDouble() const { return type() == Double; } - bool isInvalid() const { return type() == Invalid; } - - bool isSimpleType(QCborSimpleType st) const { return isSimpleType() && toSimpleType() == st; } - bool isFalse() const { return isSimpleType(QCborSimpleType::False); } - bool isTrue() const { return isSimpleType(QCborSimpleType::True); } - bool isBool() const { return isFalse() || isTrue(); } - bool isNull() const { return isSimpleType(QCborSimpleType::Null); } - bool isUndefined() const { return isSimpleType(QCborSimpleType::Undefined); } - - bool isLengthKnown() const noexcept Q_DECL_PURE_FUNCTION; - quint64 length() const; - - bool isContainer() const { return isMap() || isArray(); } - bool enterContainer() { Q_ASSERT(isContainer()); return _enterContainer_helper(); } - bool leaveContainer(); - - StringResult readString() { Q_ASSERT(isString()); return _readString_helper(); } - StringResult readByteArray(){ Q_ASSERT(isByteArray()); return _readByteArray_helper(); } - qsizetype currentStringChunkSize() const{ Q_ASSERT(isString() || isByteArray()); return _currentStringChunkSize(); } - StringResult readStringChunk(char *ptr, qsizetype maxlen); - - bool toBool() const { Q_ASSERT(isBool()); return value64 - int(QCborSimpleType::False); } - QCborTag toTag() const { Q_ASSERT(isTag()); return QCborTag(value64); } - quint64 toUnsignedInteger() const { Q_ASSERT(isUnsignedInteger()); return value64; } - QCborNegativeInteger toNegativeInteger() const { Q_ASSERT(isNegativeInteger()); return QCborNegativeInteger(value64 + 1); } - QCborSimpleType toSimpleType() const{ Q_ASSERT(isSimpleType()); return QCborSimpleType(value64); } - qfloat16 toFloat16() const { Q_ASSERT(isFloat16()); return _toFloatingPoint(); } - float toFloat() const { Q_ASSERT(isFloat()); return _toFloatingPoint(); } - double toDouble() const { Q_ASSERT(isDouble()); return _toFloatingPoint(); } - - qint64 toInteger() const - { - Q_ASSERT(isInteger()); - qint64 v = qint64(value64); - if (isNegativeInteger()) - return -v - 1; - return v; - } - -private: - void preparse(); - bool _enterContainer_helper(); - StringResult _readString_helper(); - StringResult _readByteArray_helper(); - qsizetype _currentStringChunkSize() const; - - template FP _toFloatingPoint() const noexcept - { - using UIntFP = typename QIntegerForSizeof::Unsigned; - UIntFP u = UIntFP(value64); - FP f; - memcpy(static_cast(&f), &u, sizeof(f)); - return f; - } - - friend QCborStreamReaderPrivate; - friend class QCborContainerPrivate; - quint64 value64; - QScopedPointer d; - quint8 type_; - quint8 reserved[3] = {}; -}; - QT_END_NAMESPACE -#if defined(QT_X11_DEFINES_FOUND) -# define True 1 -# define False 0 -#endif - #endif // QCBORSTREAM_H diff --git a/src/corelib/serialization/qcborstream.cpp b/src/corelib/serialization/qcborstreamreader.cpp similarity index 55% rename from src/corelib/serialization/qcborstream.cpp rename to src/corelib/serialization/qcborstreamreader.cpp index a232f7eef7..112fc6e226 100644 --- a/src/corelib/serialization/qcborstream.cpp +++ b/src/corelib/serialization/qcborstreamreader.cpp @@ -37,65 +37,36 @@ ** ****************************************************************************/ -#include "qcborstream.h" +#include "qcborstreamreader.h" + +#define CBOR_NO_ENCODER_API +#include #include #include -#include #include #include -#include QT_BEGIN_NAMESPACE -#ifdef QT_NO_DEBUG -# define NDEBUG 1 -#endif -#undef assert -#define assert Q_ASSERT - -QT_WARNING_PUSH -QT_WARNING_DISABLE_GCC("-Wunused-function") -QT_WARNING_DISABLE_CLANG("-Wunused-function") -QT_WARNING_DISABLE_CLANG("-Wundefined-internal") -QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) - -#define CBOR_ENCODER_NO_CHECK_USER - -#define CBOR_NO_VALIDATION_API 1 -#define CBOR_NO_PRETTY_API 1 -#define CBOR_API static inline -#define CBOR_PRIVATE_API static inline -#define CBOR_INLINE_API static inline - -#include - -static CborError qt_cbor_encoder_write_callback(void *token, const void *data, size_t len, CborEncoderAppendType); static bool qt_cbor_decoder_can_read(void *token, size_t len); static void qt_cbor_decoder_advance(void *token, size_t len); static void *qt_cbor_decoder_read(void *token, void *userptr, size_t offset, size_t len); static CborError qt_cbor_decoder_transfer_string(void *token, const void **userptr, size_t offset, size_t len); -#define CBOR_ENCODER_WRITER_CONTROL 1 -#define CBOR_ENCODER_WRITE_FUNCTION qt_cbor_encoder_write_callback - #define CBOR_PARSER_READER_CONTROL 1 #define CBOR_PARSER_CAN_READ_BYTES_FUNCTION qt_cbor_decoder_can_read #define CBOR_PARSER_ADVANCE_BYTES_FUNCTION qt_cbor_decoder_advance #define CBOR_PARSER_TRANSFER_STRING_FUNCTION qt_cbor_decoder_transfer_string #define CBOR_PARSER_READ_BYTES_FUNCTION qt_cbor_decoder_read -#include "../3rdparty/tinycbor/src/cborencoder.c" -#include "../3rdparty/tinycbor/src/cborerrorstrings.c" -#include "../3rdparty/tinycbor/src/cborparser.c" +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) + +#include + +QT_WARNING_POP -// silence compilers that complain about this being a static function declared -// but never defined -static CborError cbor_encoder_close_container_checked(CborEncoder*, const CborEncoder*) -{ - Q_UNREACHABLE(); - return CborErrorInternalError; -} static CborError _cbor_value_dup_string(const CborValue *, void **, size_t *, CborValue *) { Q_UNREACHABLE(); @@ -106,15 +77,6 @@ static CborError cbor_value_get_half_float_as_float(const CborValue *, float *) Q_UNREACHABLE(); return CborErrorInternalError; } -static CborError cbor_encode_float_as_half_float(CborEncoder *, float) -{ - Q_UNREACHABLE(); - return CborErrorInternalError; -} -QT_WARNING_POP - -Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(CborValue, Q_PRIMITIVE_TYPE); // confirm our constants match TinyCBOR's Q_STATIC_ASSERT(int(QCborStreamReader::UnsignedInteger) == CborIntegerType); @@ -129,1083 +91,6 @@ Q_STATIC_ASSERT(int(QCborStreamReader::Float) == CborFloatType); Q_STATIC_ASSERT(int(QCborStreamReader::Double) == CborDoubleType); Q_STATIC_ASSERT(int(QCborStreamReader::Invalid) == CborInvalidType); -/*! - \headerfile - - \brief The header contains definitions common to both the - streaming classes (QCborStreamReader and QCborStreamWriter) and to - QCborValue. - */ - -/*! - \enum QCborSimpleType - \relates - - This enum contains the possible "Simple Types" for CBOR. Simple Types range - from 0 to 255 and are types that carry no further value. - - The following values are currently known: - - \value False A "false" boolean. - \value True A "true" boolean. - \value Null Absence of value (null). - \value Undefined Missing or deleted value, usually an error. - - Qt CBOR API supports encoding and decoding any Simple Type, whether one of - those above or any other value. - - Applications should only use further values if a corresponding specification - has been published, otherwise interpretation and validation by the remote - may fail. Values 24 to 31 are reserved and must not be used. - - The current authoritative list is maintained by IANA in the - \l{https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xml}{Simple - Values registry}. - - \sa QCborStreamWriter::append(QCborSimpleType), QCborStreamReader::isSimpleType(), - QCborStreamReader::toSimpleType(), QCborValue::isSimpleType(), QCborValue::toSimpleType() - */ - -#if !defined(QT_NO_DATASTREAM) -QDataStream &operator<<(QDataStream &ds, QCborSimpleType st) -{ - return ds << quint8(st); -} - -QDataStream &operator>>(QDataStream &ds, QCborSimpleType &st) -{ - quint8 v; - ds >> v; - st = QCborSimpleType(v); - return ds; -} -#endif - -/*! - \enum QCborTag - \relates - - This enum contains no enumeration and is used only to provide type-safe - access to a CBOR tag. - - CBOR tags are 64-bit numbers that are attached to generic CBOR types to - provide further semantic meaning. QCborTag may be constructed from an - enumeration found in QCborKnownTags or directly by providing the numeric - representation. - - For example, the following creates a QCborValue containing a byte array - tagged with a tag 2. - - \snippet code/src_corelib_serialization_qcborstream.cpp 0 - - \sa QCborKnownTags, QCborStreamWriter::append(QCborTag), - QCborStreamReader::isTag(), QCborStreamReader::toTag(), - QCborValue::isTag(), QCborValue::tag() - */ - -/*! - \enum QCborKnownTags - \relates - - This enum contains a list of CBOR tags, known at the time of the Qt - implementation. This list is not meant to be complete and contains only - tags that are either backed by an RFC or specifically used by the Qt - implementation. - - The authoritative list is maintained by IANA in the - \l{https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml}{CBOR tag - registry}. - - \value DateTimeString A date and time string, formatted according to RFC 3339, as refined - by RFC 4287. It is the same format as Qt::ISODate and - Qt::ISODateWithMs. - \value UnixTime_t A numerical representation of seconds elapsed since - 1970-01-01T00:00Z. - \value PositiveBignum A positive number of arbitrary length, encoded as a byte array in - network byte order. For example, the number 2\sup{64} is represented by - a byte array containing the byte value 0x01 followed by 8 zero bytes. - \value NegativeBignum A negative number of arbirary length, encoded as the absolute value - of that number, minus one. For example, a byte array containing - byte value 0x02 followed by 8 zero bytes represents the number - -2\sup{65} - 1. - \value Decimal A decimal fraction, encoded as an array of two integers: the first - is the exponent of the power of 10, the second the integral - mantissa. The value 273.15 would be encoded as array \c{[-2, 27315]}. - \value Bigfloat Similar to Decimal, but the exponent is a power of 2 instead. - \value COSE_Encrypt0 An \c Encrypt0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} - (CBOR Object Signing and Encryption). - \value COSE_Mac0 A \c Mac0 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} - (CBOR Object Signing and Encryption). - \value COSE_Sign1 A \c Sign1 map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} - (CBOR Object Signing and Encryption). - \value ExpectedBase64url Indicates that the byte array should be encoded using Base64url - if the stream is converted to JSON. - \value ExpectedBase64 Indicates that the byte array should be encoded using Base64 - if the stream is converted to JSON. - \value ExpectedBase16 Indicates that the byte array should be encoded using Base16 (hex) - if the stream is converted to JSON. - \value EncodedCbor Indicates that the byte array contains a CBOR stream. - \value Url Indicates that the string contains a URL. - \value Base64url Indicates that the string contains data encoded using Base64url. - \value Base64 Indicates that the string contains data encoded using Base64. - \value RegularExpression Indicates that the string contains a Perl-Compatible Regular - Expression pattern. - \value MimeMessage Indicates that the string contains a MIME message (according to - \l{https://tools.ietf.org/html/rfc2045}){RFC 2045}. - \value Uuid Indicates that the byte array contains a UUID. - \value COSE_Encrypt An \c Encrypt map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} - (CBOR Object Signing and Encryption). - \value COSE_Mac A \c Mac map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} - (CBOR Object Signing and Encryption). - \value COSE_Sign A \c Sign map as specified by \l{https://tools.ietf.org/html/rfc8152}{RFC 8152} - (CBOR Object Signing and Encryption). - \value Signature No change in interpretation; this tag can be used as the outermost - tag in a CBOR stream as the file header. - - The following tags are interpreted by QCborValue during decoding and will - produce objects with extended Qt types, and it will use those tags when - encoding the same extended types. - - \value DateTimeString \l QDateTime - \value UnixTime_t \l QDateTime (only in decoding) - \value Url \l QUrl - \value Uuid \l QUuid - - Additionally, if a QCborValue containing a QByteArray is tagged using one of - \c ExpectedBase64url, \c ExpectedBase64 or \c ExpectedBase16, QCborValue - will use the expected encoding when converting to JSON (see - QCborValue::toJsonValue). - - \sa QCborTag, QCborStreamWriter::append(QCborTag), - QCborStreamReader::isTag(), QCborStreamReader::toTag(), - QCborValue::isTag(), QCborValue::tag() - */ - -/*! - \class QCborError - \inmodule QtCore - \relates - \reentrant - \since 5.12 - - \brief The QCborError class holds the error condition found while parsing or - validating a CBOR stream. - - \sa QCborStreamReader, QCborValue, QCborParserError - */ - -/*! - \enum QCborError::Code - - This enum contains the possible error condition codes. - - \value NoError No error was detected. - \value UnknownError An unknown error occurred and no further details are available. - \value AdvancePastEnd QCborStreamReader::next() was called but there are no more elements in - the current context. - \value InputOutputError An I/O error with the QIODevice occurred. - \value GarbageAtEnd Data was found in the input stream after the last element. - \value EndOfFile The end of the input stream was unexpectedly reached while processing an - element. - \value UnexpectedBreak The CBOR stream contains a Break where it is not allowed (data is - corrupt and the error is not recoverable). - \value UnknownType The CBOR stream contains an unknown/unparseable Type (data is corrupt - and the and the error is not recoverable). - \value IllegalType The CBOR stream contains a known type in a position it is not allowed - to exist (data is corrupt and the error is not recoverable). - \value IllegalNumber The CBOR stream appears to be encoding a number larger than 64-bit - (data is corrupt and the error is not recoverable). - \value IllegalSimpleType The CBOR stream contains a Simple Type encoded incorrectly (data is - corrupt and the error is not recoverable). - \value InvalidUtf8String The CBOR stream contains a text string that does not decode properly - as UTF-8 (data is corrupt and the error is not recoverable). - \value DataTooLarge CBOR string, map or array is too big and cannot be parsed by Qt - (internal limitation, but the error is not recoverable). - \value NestingTooDeep Too many levels of arrays or maps encountered while processing the - input (internal limitation, but the error is not recoverable). - \value UnsupportedType The CBOR stream contains a known type that the implementation does not - support (internal limitation, but the error is not recoverable). - */ - -// Convert from CborError to QCborError. -// -// Centralized in a function in case we need to make more adjustments in the -// future. -static QCborError fromCborError(CborError err) -{ - return { QCborError::Code(int(err)) }; -} - -// Convert to CborError from QCborError. -// -// Centralized in a function in case we need to make more adjustments in the -// future. -static CborError toCborError(QCborError c) -{ - return CborError(int(c.c)); -} - -/*! - \variable QCborError::c - \internal - */ - -/*! - \fn QCborError::operator Code() const - - Returns the error code that this QCborError object stores. - */ - -/*! - Returns a text string that matches the error code in this QCborError object. - - Note: the string is not translated. Applications whose interface allow users - to parse CBOR streams need to provide their own, translated strings. - - \sa QCborError::Code - */ -QString QCborError::toString() const -{ - switch (c) { - case NoError: - Q_STATIC_ASSERT(int(NoError) == int(CborNoError)); - return QString(); - - case UnknownError: - Q_STATIC_ASSERT(int(UnknownError) == int(CborUnknownError)); - return QStringLiteral("Unknown error"); - case AdvancePastEnd: - Q_STATIC_ASSERT(int(AdvancePastEnd) == int(CborErrorAdvancePastEOF)); - return QStringLiteral("Read past end of buffer (more bytes needed)"); - case InputOutputError: - Q_STATIC_ASSERT(int(InputOutputError) == int(CborErrorIO)); - return QStringLiteral("Input/Output error"); - case GarbageAtEnd: - Q_STATIC_ASSERT(int(GarbageAtEnd) == int(CborErrorGarbageAtEnd)); - return QStringLiteral("Data found after the end of the stream"); - case EndOfFile: - Q_STATIC_ASSERT(int(EndOfFile) == int(CborErrorUnexpectedEOF)); - return QStringLiteral("Unexpected end of input data (more bytes needed)"); - case UnexpectedBreak: - Q_STATIC_ASSERT(int(UnexpectedBreak) == int(CborErrorUnexpectedBreak)); - return QStringLiteral("Invalid CBOR stream: unexpected 'break' byte"); - case UnknownType: - Q_STATIC_ASSERT(int(UnknownType) == int(CborErrorUnknownType)); - return QStringLiteral("Invalid CBOR stream: unknown type"); - case IllegalType: - Q_STATIC_ASSERT(int(IllegalType) == int(CborErrorIllegalType)); - return QStringLiteral("Invalid CBOR stream: illegal type found"); - case IllegalNumber: - Q_STATIC_ASSERT(int(IllegalNumber) == int(CborErrorIllegalNumber)); - return QStringLiteral("Invalid CBOR stream: illegal number encoding (future extension)"); - case IllegalSimpleType: - Q_STATIC_ASSERT(int(IllegalSimpleType) == int(CborErrorIllegalSimpleType)); - return QStringLiteral("Invalid CBOR stream: illegal simple type"); - case InvalidUtf8String: - Q_STATIC_ASSERT(int(InvalidUtf8String) == int(CborErrorInvalidUtf8TextString)); - return QStringLiteral("Invalid CBOR stream: invalid UTF-8 text string"); - case DataTooLarge: - Q_STATIC_ASSERT(int(DataTooLarge) == int(CborErrorDataTooLarge)); - return QStringLiteral("Internal limitation: data set too large"); - case NestingTooDeep: - Q_STATIC_ASSERT(int(NestingTooDeep) == int(CborErrorNestingTooDeep)); - return QStringLiteral("Internal limitation: data nesting too deep"); - case UnsupportedType: - Q_STATIC_ASSERT(int(UnsupportedType) == int(CborErrorUnsupportedType)); - return QStringLiteral("Internal limitation: unsupported type"); - } - - // get the error string from TinyCBOR - CborError err = toCborError(*this); - return QString::fromLatin1(cbor_error_string(err)); -} - -/*! - \class QCborStreamWriter - \inmodule QtCore - \ingroup cbor - \reentrant - \since 5.12 - - \brief The QCborStreamWriter class is a simple CBOR encoder operating on a - one-way stream. - - This class can be used to quickly encode a stream of CBOR content directly - to either a QByteArray or QIODevice. 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, which has used it in many new RFCs. It is meant to - be used alongside the \l{https://tools.ietf.org/html/rfc7252}{CoAP - protocol}. - - QCborStreamWriter provides a StAX-like API, similar to that of - \l{QXmlStreamWriter}. It is rather low-level and requires a bit of knowledge - of CBOR encoding. For a simpler API, see \l{QCborValue} and especially the - encoding function QCborValue::toCbor(). - - The typical use of QCborStreamWriter is to create the object on the target - QByteArray or QIODevice, then call one of the append() overloads with the - desired type to be encoded. To create arrays and maps, QCborStreamWriter - provides startArray() and startMap() overloads, which must be terminated by - the corresponding endArray() and endMap() functions. - - The following example encodes the equivalent of this JSON content: - - \div{class="pre"} - { - "label": "journald", - "autoDetect": false, - "condition": "libs.journald", - "output": [ "privateFeature" ] - } - \enddiv - - \snippet code/src_corelib_serialization_qcborstream.cpp 1 - - \section1 CBOR support - - QCborStreamWriter supports all CBOR features required to create canonical - and strict streams. It implements almost all of the features specified in - \l {https://tools.ietf.org/html/rfc7049}{RFC 7049}. - - The following table lists the CBOR features that QCborStreamWriter supports. - - \table - \header \li Feature \li Support - \row \li Unsigned numbers \li Yes (full range) - \row \li Negative numbers \li Yes (full range) - \row \li Byte strings \li Yes - \row \li Text strings \li Yes - \row \li Chunked strings \li No - \row \li Tags \li Yes (arbitrary) - \row \li Booleans \li Yes - \row \li Null \li Yes - \row \li Undefined \li Yes - \row \li Arbitrary simple values \li Yes - \row \li Half-precision float (16-bit) \li Yes - \row \li Single-precision float (32-bit) \li Yes - \row \li Double-precision float (64-bit) \li Yes - \row \li Infinities and NaN floating point \li Yes - \row \li Determinate-length arrays and maps \li Yes - \row \li Indeterminate-length arrays and maps \li Yes - \row \li Map key types other than strings and integers \li Yes (arbitrary) - \endtable - - \section2 Canonical CBOR encoding - - Canonical CBOR encoding is defined by - \l{https://tools.ietf.org/html/rfc7049#section-3.9}{Section 3.9 of RFC - 7049}. Canonical encoding is not a requirement for Qt's CBOR decoding - functionality, but it may be required for some protocols. In particular, - protocols that require the ability to reproduce the same stream identically - may require this. - - In order to be considered "canonical", a CBOR stream must meet the - following requirements: - - \list - \li Integers must be as small as possible. QCborStreamWriter always - does this (no user action is required and it is not possible - to write overlong integers). - \li Array, map and string lengths must be as short as possible. As - above, QCborStreamWriter automatically does this. - \li Arrays, maps and strings must use explicit length. QCborStreamWriter - always does this for strings; for arrays and maps, be sure to call - startArray() and startMap() overloads with explicit length. - \li Keys in every map must be sorted in ascending order. QCborStreamWriter - offers no help in this item: the developer must ensure that before - calling append() for the map pairs. - \li Floating point values should be as small as possible. QCborStreamWriter - will not convert floating point values; it is up to the developer - to perform this check prior to calling append() (see those functions' - examples). - \endlist - - \section2 Strict CBOR mode - - Strict mode is defined by - \l{https://tools.ietf.org/html/rfc7049#section-3.10}{Section 3.10 of RFC - 7049}. As for Canonical encoding above, QCborStreamWriter makes it possible - to create strict CBOR streams, but does not require them or validate that - the output is so. - - \list - \li Keys in a map must be unique. QCborStreamWriter performs no validation - of map keys. - \li Tags may be required to be paired only with the correct types, - according to their specification. QCborStreamWriter performs no - validation of tag usage. - \li Text Strings must be properly-encoded UTF-8. QCborStreamWriter always - writes proper UTF-8 for strings added with append(), but performs no - validation for strings added with appendTextString(). - \endlist - - \section2 Invalid CBOR stream - - It is also possible to misuse QCborStreamWriter and produce invalid CBOR - streams that will fail to be decoded by a receiver. The following actions - will produce invalid streams: - - \list - \li Append a tag and not append the corresponding tagged value - (QCborStreamWriter produces no diagnostic). - \li Append too many or too few items to an array or map with explicit - length (endMap() and endArray() will return false and - QCborStreamWriter will log with qWarning()). - \endlist - - \sa QCborStreamReader, QCborValue, QXmlStreamWriter - */ - -class QCborStreamWriterPrivate -{ -public: - static Q_CONSTEXPR quint64 IndefiniteLength = (std::numeric_limits::max)(); - - QIODevice *device; - CborEncoder encoder; - QStack containerStack; - bool deleteDevice = false; - - QCborStreamWriterPrivate(QIODevice *device) - : device(device) - { - cbor_encoder_init_writer(&encoder, qt_cbor_encoder_write_callback, this); - } - - ~QCborStreamWriterPrivate() - { - if (deleteDevice) - delete device; - } - - template void executeAppend(CborError (*f)(CborEncoder *, Args...), Args... args) - { - f(&encoder, std::forward(args)...); - } - - void createContainer(CborError (*f)(CborEncoder *, CborEncoder *, size_t), quint64 len = IndefiniteLength) - { - Q_STATIC_ASSERT(size_t(IndefiniteLength) == CborIndefiniteLength); - if (sizeof(len) != sizeof(size_t) && len != IndefiniteLength) { - if (Q_UNLIKELY(len >= CborIndefiniteLength)) { - // TinyCBOR can't do this in 32-bit mode - qWarning("QCborStreamWriter: container of size %llu is too big for a 32-bit build; " - "will use indeterminate length instead", len); - len = CborIndefiniteLength; - } - } - - containerStack.push(encoder); - f(&containerStack.top(), &encoder, len); - } - - bool closeContainer() - { - if (containerStack.isEmpty()) { - qWarning("QCborStreamWriter: closing map or array that wasn't open"); - return false; - } - - CborEncoder container = containerStack.pop(); - CborError err = cbor_encoder_close_container(&container, &encoder); - encoder = container; - - if (Q_UNLIKELY(err)) { - if (err == CborErrorTooFewItems) - qWarning("QCborStreamWriter: not enough items added to array or map"); - else if (err == CborErrorTooManyItems) - qWarning("QCborStreamWriter: too many items added to array or map"); - return false; - } - - return true; - } -}; - -static CborError qt_cbor_encoder_write_callback(void *self, const void *data, size_t len, CborEncoderAppendType) -{ - auto that = static_cast(self); - if (!that->device) - return CborNoError; - qint64 written = that->device->write(static_cast(data), len); - return (written == qsizetype(len) ? CborNoError : CborErrorIO); -} - -/*! - Creates a QCborStreamWriter object that will write the stream to \a device. - The device must be opened before the first append() call is made. This - constructor can be used with any class that derives from QIODevice, such as - QFile, QProcess or QTcpSocket. - - QCborStreamWriter has no buffering, so every append() call will result in - one or more calls to the device's \l {QIODevice::}{write()} method. - - The following example writes an empty map to a file: - - \snippet code/src_corelib_serialization_qcborstream.cpp 2 - - QCborStreamWriter does not take ownership of \a device. - - \sa device(), setDevice() - */ -QCborStreamWriter::QCborStreamWriter(QIODevice *device) - : d(new QCborStreamWriterPrivate(device)) -{ -} - -/*! - Creates a QCborStreamWriter object that will append the stream to \a data. - All streaming is done immediately to the byte array, without the need for - flushing any buffers. - - The following example writes a number to a byte array then returns - it. - - \snippet code/src_corelib_serialization_qcborstream.cpp 3 - - QCborStreamWriter does not take ownership of \a data. - */ -QCborStreamWriter::QCborStreamWriter(QByteArray *data) - : d(new QCborStreamWriterPrivate(new QBuffer(data))) -{ - d->deleteDevice = true; - d->device->open(QIODevice::WriteOnly | QIODevice::Unbuffered); -} - -/*! - Destroys this QCborStreamWriter object and frees any resources associated. - - QCborStreamWriter does not perform error checking to see if all required - items were written to the stream prior to the object being destroyed. It is - the programmer's responsibility to ensure that it was done. - */ -QCborStreamWriter::~QCborStreamWriter() -{ -} - -/*! - Replaces the device or byte array that this QCborStreamWriter object is - writing to with \a device. - - \sa device() - */ -void QCborStreamWriter::setDevice(QIODevice *device) -{ - if (d->deleteDevice) - delete d->device; - d->device = device; - d->deleteDevice = false; -} - -/*! - Returns the QIODevice that this QCborStreamWriter object is writing to. The - device must have previously been set with either the constructor or with - setDevice(). - - If this object was created by writing to a QByteArray, this function will - return an internal instance of QBuffer, which is owned by QCborStreamWriter. - - \sa setDevice() - */ -QIODevice *QCborStreamWriter::device() const -{ - return d->device; -} - -/*! - \overload - - Appends the 64-bit unsigned value \a u to the CBOR stream, creating a CBOR - Unsigned Integer value. In the following example, we write the values 0, - 2\sup{32} and \c UINT64_MAX: - - \snippet code/src_corelib_serialization_qcborstream.cpp 4 - - \sa QCborStreamReader::isUnsignedInteger(), QCborStreamReader::toUnsignedInteger() - */ -void QCborStreamWriter::append(quint64 u) -{ - d->executeAppend(cbor_encode_uint, uint64_t(u)); -} - -/*! - \overload - - Appends the 64-bit signed value \a i to the CBOR stream. This will create - either a CBOR Unsigned Integer or CBOR NegativeInteger value based on the - sign of the parameter. In the following example, we write the values 0, -1, - 2\sup{32} and \c INT64_MAX: - - \snippet code/src_corelib_serialization_qcborstream.cpp 5 - - \sa QCborStreamReader::isInteger(), QCborStreamReader::toInteger() - */ -void QCborStreamWriter::append(qint64 i) -{ - d->executeAppend(cbor_encode_int, int64_t(i)); -} - -/*! - \overload - - Appends the 64-bit negative value \a n to the CBOR stream. - QCborNegativeInteger is a 64-bit enum that holds the absolute value of the - negative number we want to write. If n is zero, the value written will be - equivalent to 2\sup{64} (that is, -18,446,744,073,709,551,616). - - In the following example, we write the values -1, -2\sup{32} and INT64_MIN: - \snippet code/src_corelib_serialization_qcborstream.cpp 6 - - Note how this function can be used to encode numbers that cannot fit a - standard computer's 64-bit signed integer like \l qint64. That is, if \a n - is larger than \c{std::numeric_limits::max()} or is 0, this will - represent a negative number smaller than - \c{std::numeric_limits::min()}. - - \sa QCborStreamReader::isNegativeInteger(), QCborStreamReader::toNegativeInteger() - */ -void QCborStreamWriter::append(QCborNegativeInteger n) -{ - d->executeAppend(cbor_encode_negative_int, uint64_t(n)); -} - -/*! - \fn void QCborStreamWriter::append(const QByteArray &ba) - \overload - - Appends the byte array \a ba to the stream, creating a CBOR Byte String - value. QCborStreamWriter will attempt to write the entire string in one - chunk. - - The following example will load and append the contents of a file to the - stream: - - \snippet code/src_corelib_serialization_qcborstream.cpp 7 - - As the example shows, unlike JSON, CBOR requires no escaping for binary - content. - - \sa appendByteString(), QCborStreamReader::isByteArray(), - QCborStreamReader::readByteArray() - */ - -/*! - \overload - - Appends the text string \a str to the stream, creating a CBOR Text String - value. QCborStreamWriter will attempt to write the entire string in one - chunk. - - The following example appends a simple string to the stream: - - \snippet code/src_corelib_serialization_qcborstream.cpp 8 - - \b{Performance note}: CBOR requires that all Text Strings be encoded in - UTF-8, so this function will iterate over the characters in the string to - determine whether the contents are US-ASCII or not. If the string is found - to contain characters outside of US-ASCII, it will allocate memory and - convert to UTF-8. If this check is unnecessary, use appendTextString() - instead. - - \sa QCborStreamReader::isString(), QCborStreamReader::readString() - */ -void QCborStreamWriter::append(QLatin1String str) -{ - // We've got Latin-1 but CBOR wants UTF-8, so check if the string is the - // common subset (US-ASCII). - if (QtPrivate::isAscii(str)) { - // it is plain US-ASCII - appendTextString(str.latin1(), str.size()); - } else { - // non-ASCII, so we need a pass-through UTF-16 - append(QString(str)); - } -} - -/*! - \overload - - Appends the text string \a str to the stream, creating a CBOR Text String - value. QCborStreamWriter will attempt to write the entire string in one - chunk. - - The following example writes an arbitrary QString to the stream: - - \snippet code/src_corelib_serialization_qcborstream.cpp 9 - - \sa QCborStreamReader::isString(), QCborStreamReader::readString() - */ -void QCborStreamWriter::append(QStringView str) -{ - QByteArray utf8 = str.toUtf8(); - appendTextString(utf8.constData(), utf8.size()); -} - -/*! - \overload - - Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All - tags must be followed by another type which they provide meaning for. - - In the following example, we append a CBOR Tag 36 (Regular Expression) and a - QRegularExpression's pattern to the stream: - - \snippet code/src_corelib_serialization_qcborstream.cpp 10 - - \sa QCborStreamReader::isTag(), QCborStreamReader::toTag() - */ -void QCborStreamWriter::append(QCborTag tag) -{ - d->executeAppend(cbor_encode_tag, CborTag(tag)); -} - -/*! - \fn void QCborStreamWriter::append(QCborKnownTags tag) - \overload - - Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All - tags must be followed by another type which they provide meaning for. - - In the following example, we append a CBOR Tag 1 (Unix \c time_t) and an - integer representing the current time to the stream, obtained using the \c - time() function: - - \snippet code/src_corelib_serialization_qcborstream.cpp 11 - - \sa QCborStreamReader::isTag(), QCborStreamReader::toTag() - */ - -/*! - \overload - - Appends the CBOR simple type \a st to the stream, creating a CBOR Simple - Type value. In the following example, we write the simple type for Null as - well as for type 32, which Qt has no support for. - - \snippet code/src_corelib_serialization_qcborstream.cpp 12 - - \note Using Simple Types for which there is no specification can lead to - validation errors by the remote receiver. In addition, simple type values 24 - through 31 (inclusive) are reserved and must not be used. - - \sa QCborStreamReader::isSimpleType(), QCborStreamReader::toSimpleType() - */ -void QCborStreamWriter::append(QCborSimpleType st) -{ - d->executeAppend(cbor_encode_simple_value, uint8_t(st)); -} - -/*! - \overload - - Appends the floating point number \a f to the stream, creating a CBOR 16-bit - Half-Precision Floating Point value. The following code can be used to convert - a C++ \tt float to \c qfloat16 if there's no loss of precision and append it, or - instead append the \tt float. - - \snippet code/src_corelib_serialization_qcborstream.cpp 13 - - \sa QCborStreamReader::isFloat16(), QCborStreamReader::toFloat16() - */ -void QCborStreamWriter::append(qfloat16 f) -{ - d->executeAppend(cbor_encode_half_float, static_cast(&f)); -} - -/*! - \overload - - Appends the floating point number \a f to the stream, creating a CBOR 32-bit - Single-Precision Floating Point value. The following code can be used to convert - a C++ \tt double to \tt float if there's no loss of precision and append it, or - instead append the \tt double. - - \snippet code/src_corelib_serialization_qcborstream.cpp 14 - - \sa QCborStreamReader::isFloat(), QCborStreamReader::toFloat() - */ -void QCborStreamWriter::append(float f) -{ - d->executeAppend(cbor_encode_float, f); -} - -/*! - \overload - - Appends the floating point number \a d to the stream, creating a CBOR 64-bit - Double-Precision Floating Point value. QCborStreamWriter always appends the - number as-is, performing no check for whether the number is the canonical - form for NaN, an infinite, whether it is denormal or if it could be written - with a shorter format. - - The following code performs all those checks, except for the denormal one, - which is expected to be taken into account by the system FPU or floating - point emulation directly. - - \snippet code/src_corelib_serialization_qcborstream.cpp 15 - - Determining if a double can be converted to an integral with no loss of - precision is left as an exercise to the reader. - - \sa QCborStreamReader::isDouble(), QCborStreamReader::toDouble() - */ -void QCborStreamWriter::append(double d) -{ - this->d->executeAppend(cbor_encode_double, d); -} - -/*! - Appends \a len bytes of data starting from \a data to the stream, creating a - CBOR Byte String value. QCborStreamWriter will attempt to write the entire - string in one chunk. - - Unlike the QByteArray overload of append(), this function is not limited by - QByteArray's size limits. However, note that neither - QCborStreamReader::readByteArray() nor QCborValue support reading CBOR - streams with byte arrays larger than 2 GB. - - \sa append(), appendTextString(), - QCborStreamReader::isByteArray(), QCborStreamReader::readByteArray() - */ -void QCborStreamWriter::appendByteString(const char *data, qsizetype len) -{ - d->executeAppend(cbor_encode_byte_string, reinterpret_cast(data), size_t(len)); -} - -/*! - Appends \a len bytes of text starting from \a utf8 to the stream, creating a - CBOR Text String value. QCborStreamWriter will attempt to write the entire - string in one chunk. - - The string pointed to by \a utf8 is expected to be properly encoded UTF-8. - QCborStreamWriter performs no validation that this is the case. - - Unlike the QLatin1String overload of append(), this function is not limited - to 2 GB. However, note that neither QCborStreamReader::readString() nor - QCborValue support reading CBOR streams with text strings larger than 2 GB. - - \sa append(QLatin1String), append(QStringView), - QCborStreamReader::isString(), QCborStreamReader::readString() - */ -void QCborStreamWriter::appendTextString(const char *utf8, qsizetype len) -{ - d->executeAppend(cbor_encode_text_string, utf8, size_t(len)); -} - -/*! - \fn void QCborStreamWriter::append(const char *str, qsizetype size) - \overload - - Appends \a size bytes of text starting from \a str to the stream, creating a - CBOR Text String value. QCborStreamWriter will attempt to write the entire - string in one chunk. If \a size is -1, this function will write \c strlen(\a - str) bytes. - - The string pointed to by \a str is expected to be properly encoded UTF-8. - QCborStreamWriter performs no validation that this is the case. - - Unlike the QLatin1String overload of append(), this function is not limited - to 2 GB. However, note that neither QCborStreamReader nor QCborValue support - reading CBOR streams with text strings larger than 2 GB. - - \sa append(QLatin1String), append(QStringView), - QCborStreamReader::isString(), QCborStreamReader::readString() - */ - -/*! - \fn void QCborStreamWriter::append(bool b) - \overload - - Appends the boolean value \a b to the stream, creating either a CBOR False - value or a CBOR True value. This function is equivalent to (and implemented - as): - - \snippet code/src_corelib_serialization_qcborstream.cpp 16 - - \sa appendNull(), appendUndefined(), - QCborStreamReader::isBool(), QCborStreamReader::toBool() - */ - -/*! - \fn void QCborStreamWriter::append(std::nullptr_t) - \overload - - Appends a CBOR Null value to the stream. This function is equivalent to (and - implemented as): The parameter is ignored. - - \snippet code/src_corelib_serialization_qcborstream.cpp 17 - - \sa appendNull(), append(QCborSimpleType), QCborStreamReader::isNull() - */ - -/*! - \fn void QCborStreamWriter::appendNull() - - Appends a CBOR Null value to the stream. This function is equivalent to (and - implemented as): - - \snippet code/src_corelib_serialization_qcborstream.cpp 18 - - \sa append(std::nullptr_t), append(QCborSimpleType), QCborStreamReader::isNull() - */ - -/*! - \fn void QCborStreamWriter::appendUndefined() - - Appends a CBOR Undefined value to the stream. This function is equivalent to (and - implemented as): - - \snippet code/src_corelib_serialization_qcborstream.cpp 19 - - \sa append(QCborSimpleType), QCborStreamReader::isUndefined() - */ - -/*! - Starts a CBOR Array with indeterminate length in the CBOR stream. Each - startArray() call must be paired with one endArray() call and the current - CBOR element extends until the end of the array. - - The array created by this function has no explicit length. Instead, its - length is implied by the elements contained in it. Note, however, that use - of indeterminate-length arrays is not compliant with canonical CBOR encoding. - - The following example appends elements from the linked list of strings - passed as input: - - \snippet code/src_corelib_serialization_qcborstream.cpp 20 - - \sa startArray(quint64), endArray(), startMap(), QCborStreamReader::isArray(), - QCborStreamReader::isLengthKnown() - */ -void QCborStreamWriter::startArray() -{ - d->createContainer(cbor_encoder_create_array); -} - -/*! - \overload - - Starts a CBOR Array with explicit length of \a count items in the CBOR - stream. Each startArray call must be paired with one endArray() call and the - current CBOR element extends until the end of the array. - - The array created by this function has an explicit length and therefore - exactly \a count items must be added to the CBOR stream. Adding fewer or - more items will result in failure during endArray() and the CBOR stream will - be corrupt. However, explicit-length arrays are required by canonical CBOR - encoding. - - The following example appends all strings found in the \l QStringList passed as input: - - \snippet code/src_corelib_serialization_qcborstream.cpp 21 - - \b{Size limitations}: The parameter to this function is quint64, which would - seem to allow up to 2\sup{64}-1 elements in the array. However, both - QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{32}-2 - items on 32-bit systems and 2\sup{64}-2 items on 64-bit ones. Also note that - QCborArray is currently limited to 2\sup{27} elements in any platform. - - \sa startArray(), endArray(), startMap(), QCborStreamReader::isArray(), - QCborStreamReader::isLengthKnown() - */ -void QCborStreamWriter::startArray(quint64 count) -{ - d->createContainer(cbor_encoder_create_array, count); -} - -/*! - Terminates the array started by either overload of startArray() and returns - true if the correct number of elements was added to the array. This function - must be called for every startArray() used. - - A return of false indicates error in the application and an unrecoverable - error in this stream. QCborStreamWriter also writes a warning using - qWarning() if that happens. - - Calling this function when the current container is not an array is also an - error, though QCborStreamWriter cannot currently detect this condition. - - \sa startArray(), startArray(quint64), endMap() - */ -bool QCborStreamWriter::endArray() -{ - return d->closeContainer(); -} - -/*! - Starts a CBOR Map with indeterminate length in the CBOR stream. Each - startMap() call must be paired with one endMap() call and the current CBOR - element extends until the end of the map. - - The map created by this function has no explicit length. Instead, its length - is implied by the elements contained in it. Note, however, that use of - indeterminate-length maps is not compliant with canonical CBOR encoding - (canonical encoding also requires keys to be unique and in sorted order). - - The following example appends elements from the linked list of int and - string pairs passed as input: - - \snippet code/src_corelib_serialization_qcborstream.cpp 22 - - \sa startMap(quint64), endMap(), startArray(), QCborStreamReader::isMap(), - QCborStreamReader::isLengthKnown() - */ -void QCborStreamWriter::startMap() -{ - d->createContainer(cbor_encoder_create_map); -} - -/*! - \overload - - Starts a CBOR Map with explicit length of \a count items in the CBOR - stream. Each startMap call must be paired with one endMap() call and the - current CBOR element extends until the end of the map. - - The map created by this function has an explicit length and therefore - exactly \a count pairs of items must be added to the CBOR stream. Adding - fewer or more items will result in failure during endMap() and the CBOR - stream will be corrupt. However, explicit-length map are required by - canonical CBOR encoding. - - The following example appends all strings found in the \l QMap passed as input: - - \snippet code/src_corelib_serialization_qcborstream.cpp 23 - - \b{Size limitations}: The parameter to this function is quint64, which would - seem to allow up to 2\sup{64}-1 pairs in the map. However, both - QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{31}-1 - items on 32-bit systems and 2\sup{63}-1 items on 64-bit ones. Also note that - QCborMap is currently limited to 2\sup{26} elements in any platform. - - \sa startMap(), endMap(), startArray(), QCborStreamReader::isMap(), - QCborStreamReader::isLengthKnown() - */ -void QCborStreamWriter::startMap(quint64 count) -{ - d->createContainer(cbor_encoder_create_map, count); -} - -/*! - Terminates the map started by either overload of startMap() and returns - true if the correct number of elements was added to the array. This function - must be called for every startMap() used. - - A return of false indicates error in the application and an unrecoverable - error in this stream. QCborStreamWriter also writes a warning using - qWarning() if that happens. - - Calling this function when the current container is not a map is also an - error, though QCborStreamWriter cannot currently detect this condition. - - \sa startMap(), startMap(quint64), endArray() - */ -bool QCborStreamWriter::endMap() -{ - return d->closeContainer(); -} - /*! \class QCborStreamReader \inmodule QtCore @@ -1756,7 +641,7 @@ public: if (err != CborErrorUnexpectedEOF) corrupt = true; - lastError = fromCborError(err); + lastError = QCborError { QCborError::Code(int(err)) }; } void updateBufferAfterString(qsizetype offset, qsizetype size) @@ -2645,5 +1530,4 @@ QCborStreamReader::readStringChunk(char *ptr, qsizetype maxlen) QT_END_NAMESPACE -#include "moc_qcborcommon.cpp" -#include "moc_qcborstream.cpp" +#include "moc_qcborstreamreader.cpp" diff --git a/src/corelib/serialization/qcborstreamreader.h b/src/corelib/serialization/qcborstreamreader.h new file mode 100644 index 0000000000..6d5feccfcf --- /dev/null +++ b/src/corelib/serialization/qcborstreamreader.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** 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 QCBORSTREAMREADER_H +#define QCBORSTREAMREADER_H + +#include +#include +#include +#include +#include +#include + +QT_REQUIRE_CONFIG(cborstreamreader); + +// See qcborcommon.h for why we check +#if defined(QT_X11_DEFINES_FOUND) +# undef True +# undef False +#endif + +QT_BEGIN_NAMESPACE + +class QIODevice; + +class QCborStreamReaderPrivate; +class Q_CORE_EXPORT QCborStreamReader +{ + Q_GADGET +public: + enum Type : quint8 { + UnsignedInteger = 0x00, + NegativeInteger = 0x20, + ByteString = 0x40, + ByteArray = ByteString, + TextString = 0x60, + String = TextString, + Array = 0x80, + Map = 0xa0, + Tag = 0xc0, + SimpleType = 0xe0, + HalfFloat = 0xf9, + Float16 = HalfFloat, + Float = 0xfa, + Double = 0xfb, + + Invalid = 0xff + }; + Q_ENUM(Type) + + enum StringResultCode { + EndOfString = 0, + Ok = 1, + Error = -1 + }; + template struct StringResult { + Container data; + StringResultCode status = Error; + }; + Q_ENUM(StringResultCode) + + QCborStreamReader(); + QCborStreamReader(const char *data, qsizetype len); + QCborStreamReader(const quint8 *data, qsizetype len); + explicit QCborStreamReader(const QByteArray &data); + explicit QCborStreamReader(QIODevice *device); + ~QCborStreamReader(); + Q_DISABLE_COPY(QCborStreamReader) + + void setDevice(QIODevice *device); + QIODevice *device() const; + void addData(const QByteArray &data); + void addData(const char *data, qsizetype len); + void addData(const quint8 *data, qsizetype len) + { addData(reinterpret_cast(data), len); } + void reparse(); + void clear(); + void reset(); + + QCborError lastError(); + + qint64 currentOffset() const; + + bool isValid() const { return !isInvalid(); } + + int containerDepth() const; + QCborStreamReader::Type parentContainerType() const; + bool hasNext() const noexcept Q_DECL_PURE_FUNCTION; + bool next(int maxRecursion = 10000); + + Type type() const { return QCborStreamReader::Type(type_); } + bool isUnsignedInteger() const { return type() == UnsignedInteger; } + bool isNegativeInteger() const { return type() == NegativeInteger; } + bool isInteger() const { return quint8(type()) <= quint8(NegativeInteger); } + bool isByteArray() const { return type() == ByteArray; } + bool isString() const { return type() == String; } + bool isArray() const { return type() == Array; } + bool isMap() const { return type() == Map; } + bool isTag() const { return type() == Tag; } + bool isSimpleType() const { return type() == SimpleType; } + bool isFloat16() const { return type() == Float16; } + bool isFloat() const { return type() == Float; } + bool isDouble() const { return type() == Double; } + bool isInvalid() const { return type() == Invalid; } + + bool isSimpleType(QCborSimpleType st) const { return isSimpleType() && toSimpleType() == st; } + bool isFalse() const { return isSimpleType(QCborSimpleType::False); } + bool isTrue() const { return isSimpleType(QCborSimpleType::True); } + bool isBool() const { return isFalse() || isTrue(); } + bool isNull() const { return isSimpleType(QCborSimpleType::Null); } + bool isUndefined() const { return isSimpleType(QCborSimpleType::Undefined); } + + bool isLengthKnown() const noexcept Q_DECL_PURE_FUNCTION; + quint64 length() const; + + bool isContainer() const { return isMap() || isArray(); } + bool enterContainer() { Q_ASSERT(isContainer()); return _enterContainer_helper(); } + bool leaveContainer(); + + StringResult readString() { Q_ASSERT(isString()); return _readString_helper(); } + StringResult readByteArray(){ Q_ASSERT(isByteArray()); return _readByteArray_helper(); } + qsizetype currentStringChunkSize() const{ Q_ASSERT(isString() || isByteArray()); return _currentStringChunkSize(); } + StringResult readStringChunk(char *ptr, qsizetype maxlen); + + bool toBool() const { Q_ASSERT(isBool()); return value64 - int(QCborSimpleType::False); } + QCborTag toTag() const { Q_ASSERT(isTag()); return QCborTag(value64); } + quint64 toUnsignedInteger() const { Q_ASSERT(isUnsignedInteger()); return value64; } + QCborNegativeInteger toNegativeInteger() const { Q_ASSERT(isNegativeInteger()); return QCborNegativeInteger(value64 + 1); } + QCborSimpleType toSimpleType() const{ Q_ASSERT(isSimpleType()); return QCborSimpleType(value64); } + qfloat16 toFloat16() const { Q_ASSERT(isFloat16()); return _toFloatingPoint(); } + float toFloat() const { Q_ASSERT(isFloat()); return _toFloatingPoint(); } + double toDouble() const { Q_ASSERT(isDouble()); return _toFloatingPoint(); } + + qint64 toInteger() const + { + Q_ASSERT(isInteger()); + qint64 v = qint64(value64); + if (isNegativeInteger()) + return -v - 1; + return v; + } + +private: + void preparse(); + bool _enterContainer_helper(); + StringResult _readString_helper(); + StringResult _readByteArray_helper(); + qsizetype _currentStringChunkSize() const; + + template FP _toFloatingPoint() const noexcept + { + using UIntFP = typename QIntegerForSizeof::Unsigned; + UIntFP u = UIntFP(value64); + FP f; + memcpy(static_cast(&f), &u, sizeof(f)); + return f; + } + + friend QCborStreamReaderPrivate; + friend class QCborContainerPrivate; + quint64 value64; + QScopedPointer d; + quint8 type_; + quint8 reserved[3] = {}; +}; + +QT_END_NAMESPACE + +#if defined(QT_X11_DEFINES_FOUND) +# define True 1 +# define False 0 +#endif + +#endif // QCBORSTREAMREADER_H diff --git a/src/corelib/serialization/qcborstreamwriter.cpp b/src/corelib/serialization/qcborstreamwriter.cpp new file mode 100644 index 0000000000..d7f750b05e --- /dev/null +++ b/src/corelib/serialization/qcborstreamwriter.cpp @@ -0,0 +1,868 @@ +/**************************************************************************** +** +** 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 "qcborstreamwriter.h" + +#define CBOR_NO_PARSER_API +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static CborError qt_cbor_encoder_write_callback(void *token, const void *data, size_t len, CborEncoderAppendType); +#define CBOR_ENCODER_WRITER_CONTROL 1 +#define CBOR_ENCODER_WRITE_FUNCTION qt_cbor_encoder_write_callback +#define CBOR_ENCODER_NO_CHECK_USER + +QT_WARNING_PUSH +QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?) + +#include + +QT_WARNING_POP + +// silence compilers that complain about this being a static function declared +// but never defined +static CborError cbor_encoder_close_container_checked(CborEncoder*, const CborEncoder*) +{ + Q_UNREACHABLE(); + return CborErrorInternalError; +} + +static CborError cbor_encode_float_as_half_float(CborEncoder *, float) +{ + Q_UNREACHABLE(); + return CborErrorInternalError; +} + +Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE); + +/*! + \class QCborStreamWriter + \inmodule QtCore + \ingroup cbor + \reentrant + \since 5.12 + + \brief The QCborStreamWriter class is a simple CBOR encoder operating on a + one-way stream. + + This class can be used to quickly encode a stream of CBOR content directly + to either a QByteArray or QIODevice. 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, which has used it in many new RFCs. It is meant to + be used alongside the \l{https://tools.ietf.org/html/rfc7252}{CoAP + protocol}. + + QCborStreamWriter provides a StAX-like API, similar to that of + \l{QXmlStreamWriter}. It is rather low-level and requires a bit of knowledge + of CBOR encoding. For a simpler API, see \l{QCborValue} and especially the + encoding function QCborValue::toCbor(). + + The typical use of QCborStreamWriter is to create the object on the target + QByteArray or QIODevice, then call one of the append() overloads with the + desired type to be encoded. To create arrays and maps, QCborStreamWriter + provides startArray() and startMap() overloads, which must be terminated by + the corresponding endArray() and endMap() functions. + + The following example encodes the equivalent of this JSON content: + + \div{class="pre"} + { + "label": "journald", + "autoDetect": false, + "condition": "libs.journald", + "output": [ "privateFeature" ] + } + \enddiv + + \snippet code/src_corelib_serialization_qcborstream.cpp 1 + + \section1 CBOR support + + QCborStreamWriter supports all CBOR features required to create canonical + and strict streams. It implements almost all of the features specified in + \l {https://tools.ietf.org/html/rfc7049}{RFC 7049}. + + The following table lists the CBOR features that QCborStreamWriter supports. + + \table + \header \li Feature \li Support + \row \li Unsigned numbers \li Yes (full range) + \row \li Negative numbers \li Yes (full range) + \row \li Byte strings \li Yes + \row \li Text strings \li Yes + \row \li Chunked strings \li No + \row \li Tags \li Yes (arbitrary) + \row \li Booleans \li Yes + \row \li Null \li Yes + \row \li Undefined \li Yes + \row \li Arbitrary simple values \li Yes + \row \li Half-precision float (16-bit) \li Yes + \row \li Single-precision float (32-bit) \li Yes + \row \li Double-precision float (64-bit) \li Yes + \row \li Infinities and NaN floating point \li Yes + \row \li Determinate-length arrays and maps \li Yes + \row \li Indeterminate-length arrays and maps \li Yes + \row \li Map key types other than strings and integers \li Yes (arbitrary) + \endtable + + \section2 Canonical CBOR encoding + + Canonical CBOR encoding is defined by + \l{https://tools.ietf.org/html/rfc7049#section-3.9}{Section 3.9 of RFC + 7049}. Canonical encoding is not a requirement for Qt's CBOR decoding + functionality, but it may be required for some protocols. In particular, + protocols that require the ability to reproduce the same stream identically + may require this. + + In order to be considered "canonical", a CBOR stream must meet the + following requirements: + + \list + \li Integers must be as small as possible. QCborStreamWriter always + does this (no user action is required and it is not possible + to write overlong integers). + \li Array, map and string lengths must be as short as possible. As + above, QCborStreamWriter automatically does this. + \li Arrays, maps and strings must use explicit length. QCborStreamWriter + always does this for strings; for arrays and maps, be sure to call + startArray() and startMap() overloads with explicit length. + \li Keys in every map must be sorted in ascending order. QCborStreamWriter + offers no help in this item: the developer must ensure that before + calling append() for the map pairs. + \li Floating point values should be as small as possible. QCborStreamWriter + will not convert floating point values; it is up to the developer + to perform this check prior to calling append() (see those functions' + examples). + \endlist + + \section2 Strict CBOR mode + + Strict mode is defined by + \l{https://tools.ietf.org/html/rfc7049#section-3.10}{Section 3.10 of RFC + 7049}. As for Canonical encoding above, QCborStreamWriter makes it possible + to create strict CBOR streams, but does not require them or validate that + the output is so. + + \list + \li Keys in a map must be unique. QCborStreamWriter performs no validation + of map keys. + \li Tags may be required to be paired only with the correct types, + according to their specification. QCborStreamWriter performs no + validation of tag usage. + \li Text Strings must be properly-encoded UTF-8. QCborStreamWriter always + writes proper UTF-8 for strings added with append(), but performs no + validation for strings added with appendTextString(). + \endlist + + \section2 Invalid CBOR stream + + It is also possible to misuse QCborStreamWriter and produce invalid CBOR + streams that will fail to be decoded by a receiver. The following actions + will produce invalid streams: + + \list + \li Append a tag and not append the corresponding tagged value + (QCborStreamWriter produces no diagnostic). + \li Append too many or too few items to an array or map with explicit + length (endMap() and endArray() will return false and + QCborStreamWriter will log with qWarning()). + \endlist + + \sa QCborStreamReader, QCborValue, QXmlStreamWriter + */ + +class QCborStreamWriterPrivate +{ +public: + static Q_CONSTEXPR quint64 IndefiniteLength = (std::numeric_limits::max)(); + + QIODevice *device; + CborEncoder encoder; + QStack containerStack; + bool deleteDevice = false; + + QCborStreamWriterPrivate(QIODevice *device) + : device(device) + { + cbor_encoder_init_writer(&encoder, qt_cbor_encoder_write_callback, this); + } + + ~QCborStreamWriterPrivate() + { + if (deleteDevice) + delete device; + } + + template void executeAppend(CborError (*f)(CborEncoder *, Args...), Args... args) + { + f(&encoder, std::forward(args)...); + } + + void createContainer(CborError (*f)(CborEncoder *, CborEncoder *, size_t), quint64 len = IndefiniteLength) + { + Q_STATIC_ASSERT(size_t(IndefiniteLength) == CborIndefiniteLength); + if (sizeof(len) != sizeof(size_t) && len != IndefiniteLength) { + if (Q_UNLIKELY(len >= CborIndefiniteLength)) { + // TinyCBOR can't do this in 32-bit mode + qWarning("QCborStreamWriter: container of size %llu is too big for a 32-bit build; " + "will use indeterminate length instead", len); + len = CborIndefiniteLength; + } + } + + containerStack.push(encoder); + f(&containerStack.top(), &encoder, len); + } + + bool closeContainer() + { + if (containerStack.isEmpty()) { + qWarning("QCborStreamWriter: closing map or array that wasn't open"); + return false; + } + + CborEncoder container = containerStack.pop(); + CborError err = cbor_encoder_close_container(&container, &encoder); + encoder = container; + + if (Q_UNLIKELY(err)) { + if (err == CborErrorTooFewItems) + qWarning("QCborStreamWriter: not enough items added to array or map"); + else if (err == CborErrorTooManyItems) + qWarning("QCborStreamWriter: too many items added to array or map"); + return false; + } + + return true; + } +}; + +static CborError qt_cbor_encoder_write_callback(void *self, const void *data, size_t len, CborEncoderAppendType) +{ + auto that = static_cast(self); + if (!that->device) + return CborNoError; + qint64 written = that->device->write(static_cast(data), len); + return (written == qsizetype(len) ? CborNoError : CborErrorIO); +} + +/*! + Creates a QCborStreamWriter object that will write the stream to \a device. + The device must be opened before the first append() call is made. This + constructor can be used with any class that derives from QIODevice, such as + QFile, QProcess or QTcpSocket. + + QCborStreamWriter has no buffering, so every append() call will result in + one or more calls to the device's \l {QIODevice::}{write()} method. + + The following example writes an empty map to a file: + + \snippet code/src_corelib_serialization_qcborstream.cpp 2 + + QCborStreamWriter does not take ownership of \a device. + + \sa device(), setDevice() + */ +QCborStreamWriter::QCborStreamWriter(QIODevice *device) + : d(new QCborStreamWriterPrivate(device)) +{ +} + +/*! + Creates a QCborStreamWriter object that will append the stream to \a data. + All streaming is done immediately to the byte array, without the need for + flushing any buffers. + + The following example writes a number to a byte array then returns + it. + + \snippet code/src_corelib_serialization_qcborstream.cpp 3 + + QCborStreamWriter does not take ownership of \a data. + */ +QCborStreamWriter::QCborStreamWriter(QByteArray *data) + : d(new QCborStreamWriterPrivate(new QBuffer(data))) +{ + d->deleteDevice = true; + d->device->open(QIODevice::WriteOnly | QIODevice::Unbuffered); +} + +/*! + Destroys this QCborStreamWriter object and frees any resources associated. + + QCborStreamWriter does not perform error checking to see if all required + items were written to the stream prior to the object being destroyed. It is + the programmer's responsibility to ensure that it was done. + */ +QCborStreamWriter::~QCborStreamWriter() +{ +} + +/*! + Replaces the device or byte array that this QCborStreamWriter object is + writing to with \a device. + + \sa device() + */ +void QCborStreamWriter::setDevice(QIODevice *device) +{ + if (d->deleteDevice) + delete d->device; + d->device = device; + d->deleteDevice = false; +} + +/*! + Returns the QIODevice that this QCborStreamWriter object is writing to. The + device must have previously been set with either the constructor or with + setDevice(). + + If this object was created by writing to a QByteArray, this function will + return an internal instance of QBuffer, which is owned by QCborStreamWriter. + + \sa setDevice() + */ +QIODevice *QCborStreamWriter::device() const +{ + return d->device; +} + +/*! + \overload + + Appends the 64-bit unsigned value \a u to the CBOR stream, creating a CBOR + Unsigned Integer value. In the following example, we write the values 0, + 2\sup{32} and \c UINT64_MAX: + + \snippet code/src_corelib_serialization_qcborstream.cpp 4 + + \sa QCborStreamReader::isUnsignedInteger(), QCborStreamReader::toUnsignedInteger() + */ +void QCborStreamWriter::append(quint64 u) +{ + d->executeAppend(cbor_encode_uint, uint64_t(u)); +} + +/*! + \overload + + Appends the 64-bit signed value \a i to the CBOR stream. This will create + either a CBOR Unsigned Integer or CBOR NegativeInteger value based on the + sign of the parameter. In the following example, we write the values 0, -1, + 2\sup{32} and \c INT64_MAX: + + \snippet code/src_corelib_serialization_qcborstream.cpp 5 + + \sa QCborStreamReader::isInteger(), QCborStreamReader::toInteger() + */ +void QCborStreamWriter::append(qint64 i) +{ + d->executeAppend(cbor_encode_int, int64_t(i)); +} + +/*! + \overload + + Appends the 64-bit negative value \a n to the CBOR stream. + QCborNegativeInteger is a 64-bit enum that holds the absolute value of the + negative number we want to write. If n is zero, the value written will be + equivalent to 2\sup{64} (that is, -18,446,744,073,709,551,616). + + In the following example, we write the values -1, -2\sup{32} and INT64_MIN: + \snippet code/src_corelib_serialization_qcborstream.cpp 6 + + Note how this function can be used to encode numbers that cannot fit a + standard computer's 64-bit signed integer like \l qint64. That is, if \a n + is larger than \c{std::numeric_limits::max()} or is 0, this will + represent a negative number smaller than + \c{std::numeric_limits::min()}. + + \sa QCborStreamReader::isNegativeInteger(), QCborStreamReader::toNegativeInteger() + */ +void QCborStreamWriter::append(QCborNegativeInteger n) +{ + d->executeAppend(cbor_encode_negative_int, uint64_t(n)); +} + +/*! + \fn void QCborStreamWriter::append(const QByteArray &ba) + \overload + + Appends the byte array \a ba to the stream, creating a CBOR Byte String + value. QCborStreamWriter will attempt to write the entire string in one + chunk. + + The following example will load and append the contents of a file to the + stream: + + \snippet code/src_corelib_serialization_qcborstream.cpp 7 + + As the example shows, unlike JSON, CBOR requires no escaping for binary + content. + + \sa appendByteString(), QCborStreamReader::isByteArray(), + QCborStreamReader::readByteArray() + */ + +/*! + \overload + + Appends the text string \a str to the stream, creating a CBOR Text String + value. QCborStreamWriter will attempt to write the entire string in one + chunk. + + The following example appends a simple string to the stream: + + \snippet code/src_corelib_serialization_qcborstream.cpp 8 + + \b{Performance note}: CBOR requires that all Text Strings be encoded in + UTF-8, so this function will iterate over the characters in the string to + determine whether the contents are US-ASCII or not. If the string is found + to contain characters outside of US-ASCII, it will allocate memory and + convert to UTF-8. If this check is unnecessary, use appendTextString() + instead. + + \sa QCborStreamReader::isString(), QCborStreamReader::readString() + */ +void QCborStreamWriter::append(QLatin1String str) +{ + // We've got Latin-1 but CBOR wants UTF-8, so check if the string is the + // common subset (US-ASCII). + if (QtPrivate::isAscii(str)) { + // it is plain US-ASCII + appendTextString(str.latin1(), str.size()); + } else { + // non-ASCII, so we need a pass-through UTF-16 + append(QString(str)); + } +} + +/*! + \overload + + Appends the text string \a str to the stream, creating a CBOR Text String + value. QCborStreamWriter will attempt to write the entire string in one + chunk. + + The following example writes an arbitrary QString to the stream: + + \snippet code/src_corelib_serialization_qcborstream.cpp 9 + + \sa QCborStreamReader::isString(), QCborStreamReader::readString() + */ +void QCborStreamWriter::append(QStringView str) +{ + QByteArray utf8 = str.toUtf8(); + appendTextString(utf8.constData(), utf8.size()); +} + +/*! + \overload + + Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All + tags must be followed by another type which they provide meaning for. + + In the following example, we append a CBOR Tag 36 (Regular Expression) and a + QRegularExpression's pattern to the stream: + + \snippet code/src_corelib_serialization_qcborstream.cpp 10 + + \sa QCborStreamReader::isTag(), QCborStreamReader::toTag() + */ +void QCborStreamWriter::append(QCborTag tag) +{ + d->executeAppend(cbor_encode_tag, CborTag(tag)); +} + +/*! + \fn void QCborStreamWriter::append(QCborKnownTags tag) + \overload + + Appends the CBOR tag \a tag to the stream, creating a CBOR Tag value. All + tags must be followed by another type which they provide meaning for. + + In the following example, we append a CBOR Tag 1 (Unix \c time_t) and an + integer representing the current time to the stream, obtained using the \c + time() function: + + \snippet code/src_corelib_serialization_qcborstream.cpp 11 + + \sa QCborStreamReader::isTag(), QCborStreamReader::toTag() + */ + +/*! + \overload + + Appends the CBOR simple type \a st to the stream, creating a CBOR Simple + Type value. In the following example, we write the simple type for Null as + well as for type 32, which Qt has no support for. + + \snippet code/src_corelib_serialization_qcborstream.cpp 12 + + \note Using Simple Types for which there is no specification can lead to + validation errors by the remote receiver. In addition, simple type values 24 + through 31 (inclusive) are reserved and must not be used. + + \sa QCborStreamReader::isSimpleType(), QCborStreamReader::toSimpleType() + */ +void QCborStreamWriter::append(QCborSimpleType st) +{ + d->executeAppend(cbor_encode_simple_value, uint8_t(st)); +} + +#ifndef QT_BOOTSTRAPPED +/*! + \overload + + Appends the floating point number \a f to the stream, creating a CBOR 16-bit + Half-Precision Floating Point value. The following code can be used to convert + a C++ \tt float to \c qfloat16 if there's no loss of precision and append it, or + instead append the \tt float. + + \snippet code/src_corelib_serialization_qcborstream.cpp 13 + + \sa QCborStreamReader::isFloat16(), QCborStreamReader::toFloat16() + */ +void QCborStreamWriter::append(qfloat16 f) +{ + d->executeAppend(cbor_encode_half_float, static_cast(&f)); +} +#endif // QT_BOOTSTRAPPED + +/*! + \overload + + Appends the floating point number \a f to the stream, creating a CBOR 32-bit + Single-Precision Floating Point value. The following code can be used to convert + a C++ \tt double to \tt float if there's no loss of precision and append it, or + instead append the \tt double. + + \snippet code/src_corelib_serialization_qcborstream.cpp 14 + + \sa QCborStreamReader::isFloat(), QCborStreamReader::toFloat() + */ +void QCborStreamWriter::append(float f) +{ + d->executeAppend(cbor_encode_float, f); +} + +/*! + \overload + + Appends the floating point number \a d to the stream, creating a CBOR 64-bit + Double-Precision Floating Point value. QCborStreamWriter always appends the + number as-is, performing no check for whether the number is the canonical + form for NaN, an infinite, whether it is denormal or if it could be written + with a shorter format. + + The following code performs all those checks, except for the denormal one, + which is expected to be taken into account by the system FPU or floating + point emulation directly. + + \snippet code/src_corelib_serialization_qcborstream.cpp 15 + + Determining if a double can be converted to an integral with no loss of + precision is left as an exercise to the reader. + + \sa QCborStreamReader::isDouble(), QCborStreamReader::toDouble() + */ +void QCborStreamWriter::append(double d) +{ + this->d->executeAppend(cbor_encode_double, d); +} + +/*! + Appends \a len bytes of data starting from \a data to the stream, creating a + CBOR Byte String value. QCborStreamWriter will attempt to write the entire + string in one chunk. + + Unlike the QByteArray overload of append(), this function is not limited by + QByteArray's size limits. However, note that neither + QCborStreamReader::readByteArray() nor QCborValue support reading CBOR + streams with byte arrays larger than 2 GB. + + \sa append(), appendTextString(), + QCborStreamReader::isByteArray(), QCborStreamReader::readByteArray() + */ +void QCborStreamWriter::appendByteString(const char *data, qsizetype len) +{ + d->executeAppend(cbor_encode_byte_string, reinterpret_cast(data), size_t(len)); +} + +/*! + Appends \a len bytes of text starting from \a utf8 to the stream, creating a + CBOR Text String value. QCborStreamWriter will attempt to write the entire + string in one chunk. + + The string pointed to by \a utf8 is expected to be properly encoded UTF-8. + QCborStreamWriter performs no validation that this is the case. + + Unlike the QLatin1String overload of append(), this function is not limited + to 2 GB. However, note that neither QCborStreamReader::readString() nor + QCborValue support reading CBOR streams with text strings larger than 2 GB. + + \sa append(QLatin1String), append(QStringView), + QCborStreamReader::isString(), QCborStreamReader::readString() + */ +void QCborStreamWriter::appendTextString(const char *utf8, qsizetype len) +{ + d->executeAppend(cbor_encode_text_string, utf8, size_t(len)); +} + +/*! + \fn void QCborStreamWriter::append(const char *str, qsizetype size) + \overload + + Appends \a size bytes of text starting from \a str to the stream, creating a + CBOR Text String value. QCborStreamWriter will attempt to write the entire + string in one chunk. If \a size is -1, this function will write \c strlen(\a + str) bytes. + + The string pointed to by \a str is expected to be properly encoded UTF-8. + QCborStreamWriter performs no validation that this is the case. + + Unlike the QLatin1String overload of append(), this function is not limited + to 2 GB. However, note that neither QCborStreamReader nor QCborValue support + reading CBOR streams with text strings larger than 2 GB. + + \sa append(QLatin1String), append(QStringView), + QCborStreamReader::isString(), QCborStreamReader::readString() + */ + +/*! + \fn void QCborStreamWriter::append(bool b) + \overload + + Appends the boolean value \a b to the stream, creating either a CBOR False + value or a CBOR True value. This function is equivalent to (and implemented + as): + + \snippet code/src_corelib_serialization_qcborstream.cpp 16 + + \sa appendNull(), appendUndefined(), + QCborStreamReader::isBool(), QCborStreamReader::toBool() + */ + +/*! + \fn void QCborStreamWriter::append(std::nullptr_t) + \overload + + Appends a CBOR Null value to the stream. This function is equivalent to (and + implemented as): The parameter is ignored. + + \snippet code/src_corelib_serialization_qcborstream.cpp 17 + + \sa appendNull(), append(QCborSimpleType), QCborStreamReader::isNull() + */ + +/*! + \fn void QCborStreamWriter::appendNull() + + Appends a CBOR Null value to the stream. This function is equivalent to (and + implemented as): + + \snippet code/src_corelib_serialization_qcborstream.cpp 18 + + \sa append(std::nullptr_t), append(QCborSimpleType), QCborStreamReader::isNull() + */ + +/*! + \fn void QCborStreamWriter::appendUndefined() + + Appends a CBOR Undefined value to the stream. This function is equivalent to (and + implemented as): + + \snippet code/src_corelib_serialization_qcborstream.cpp 19 + + \sa append(QCborSimpleType), QCborStreamReader::isUndefined() + */ + +/*! + Starts a CBOR Array with indeterminate length in the CBOR stream. Each + startArray() call must be paired with one endArray() call and the current + CBOR element extends until the end of the array. + + The array created by this function has no explicit length. Instead, its + length is implied by the elements contained in it. Note, however, that use + of indeterminate-length arrays is not compliant with canonical CBOR encoding. + + The following example appends elements from the linked list of strings + passed as input: + + \snippet code/src_corelib_serialization_qcborstream.cpp 20 + + \sa startArray(quint64), endArray(), startMap(), QCborStreamReader::isArray(), + QCborStreamReader::isLengthKnown() + */ +void QCborStreamWriter::startArray() +{ + d->createContainer(cbor_encoder_create_array); +} + +/*! + \overload + + Starts a CBOR Array with explicit length of \a count items in the CBOR + stream. Each startArray call must be paired with one endArray() call and the + current CBOR element extends until the end of the array. + + The array created by this function has an explicit length and therefore + exactly \a count items must be added to the CBOR stream. Adding fewer or + more items will result in failure during endArray() and the CBOR stream will + be corrupt. However, explicit-length arrays are required by canonical CBOR + encoding. + + The following example appends all strings found in the \l QStringList passed as input: + + \snippet code/src_corelib_serialization_qcborstream.cpp 21 + + \b{Size limitations}: The parameter to this function is quint64, which would + seem to allow up to 2\sup{64}-1 elements in the array. However, both + QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{32}-2 + items on 32-bit systems and 2\sup{64}-2 items on 64-bit ones. Also note that + QCborArray is currently limited to 2\sup{27} elements in any platform. + + \sa startArray(), endArray(), startMap(), QCborStreamReader::isArray(), + QCborStreamReader::isLengthKnown() + */ +void QCborStreamWriter::startArray(quint64 count) +{ + d->createContainer(cbor_encoder_create_array, count); +} + +/*! + Terminates the array started by either overload of startArray() and returns + true if the correct number of elements was added to the array. This function + must be called for every startArray() used. + + A return of false indicates error in the application and an unrecoverable + error in this stream. QCborStreamWriter also writes a warning using + qWarning() if that happens. + + Calling this function when the current container is not an array is also an + error, though QCborStreamWriter cannot currently detect this condition. + + \sa startArray(), startArray(quint64), endMap() + */ +bool QCborStreamWriter::endArray() +{ + return d->closeContainer(); +} + +/*! + Starts a CBOR Map with indeterminate length in the CBOR stream. Each + startMap() call must be paired with one endMap() call and the current CBOR + element extends until the end of the map. + + The map created by this function has no explicit length. Instead, its length + is implied by the elements contained in it. Note, however, that use of + indeterminate-length maps is not compliant with canonical CBOR encoding + (canonical encoding also requires keys to be unique and in sorted order). + + The following example appends elements from the linked list of int and + string pairs passed as input: + + \snippet code/src_corelib_serialization_qcborstream.cpp 22 + + \sa startMap(quint64), endMap(), startArray(), QCborStreamReader::isMap(), + QCborStreamReader::isLengthKnown() + */ +void QCborStreamWriter::startMap() +{ + d->createContainer(cbor_encoder_create_map); +} + +/*! + \overload + + Starts a CBOR Map with explicit length of \a count items in the CBOR + stream. Each startMap call must be paired with one endMap() call and the + current CBOR element extends until the end of the map. + + The map created by this function has an explicit length and therefore + exactly \a count pairs of items must be added to the CBOR stream. Adding + fewer or more items will result in failure during endMap() and the CBOR + stream will be corrupt. However, explicit-length map are required by + canonical CBOR encoding. + + The following example appends all strings found in the \l QMap passed as input: + + \snippet code/src_corelib_serialization_qcborstream.cpp 23 + + \b{Size limitations}: The parameter to this function is quint64, which would + seem to allow up to 2\sup{64}-1 pairs in the map. However, both + QCborStreamWriter and QCborStreamReader are currently limited to 2\sup{31}-1 + items on 32-bit systems and 2\sup{63}-1 items on 64-bit ones. Also note that + QCborMap is currently limited to 2\sup{26} elements in any platform. + + \sa startMap(), endMap(), startArray(), QCborStreamReader::isMap(), + QCborStreamReader::isLengthKnown() + */ +void QCborStreamWriter::startMap(quint64 count) +{ + d->createContainer(cbor_encoder_create_map, count); +} + +/*! + Terminates the map started by either overload of startMap() and returns + true if the correct number of elements was added to the array. This function + must be called for every startMap() used. + + A return of false indicates error in the application and an unrecoverable + error in this stream. QCborStreamWriter also writes a warning using + qWarning() if that happens. + + Calling this function when the current container is not a map is also an + error, though QCborStreamWriter cannot currently detect this condition. + + \sa startMap(), startMap(quint64), endArray() + */ +bool QCborStreamWriter::endMap() +{ + return d->closeContainer(); +} + +QT_END_NAMESPACE diff --git a/src/corelib/serialization/qcborstreamwriter.h b/src/corelib/serialization/qcborstreamwriter.h new file mode 100644 index 0000000000..f8c94ceb93 --- /dev/null +++ b/src/corelib/serialization/qcborstreamwriter.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** 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 QCBORSTREAMWRITER_H +#define QCBORSTREAMWRITER_H + +#include +#include +#include +#include +#include +#ifndef QT_BOOTSTRAPPED +#include +#endif + +QT_REQUIRE_CONFIG(cborstreamwriter); + +// See qcborcommon.h for why we check +#if defined(QT_X11_DEFINES_FOUND) +# undef True +# undef False +#endif + +QT_BEGIN_NAMESPACE + +class QIODevice; + +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); } +#ifndef QT_BOOTSTRAPPED + void append(qfloat16 f); +#endif + 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 d; +}; + +QT_END_NAMESPACE + +#if defined(QT_X11_DEFINES_FOUND) +# define True 1 +# define False 0 +#endif + +#endif // QCBORSTREAMWRITER_H diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 61d61dd9ae..db2840704c 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -43,8 +43,12 @@ #include "qcborarray.h" #include "qcbormap.h" -#if QT_CONFIG(cborstream) -#include "qcborstream.h" +#if QT_CONFIG(cborstreamreader) +#include "qcborstreamreader.h" +#endif + +#if QT_CONFIG(cborstreamwriter) +#include "qcborstreamwriter.h" #endif #include @@ -840,16 +844,20 @@ static QCborValue::Type convertToExtendedType(QCborContainerPrivate *d) return QCborValue::Tag; } -#if QT_CONFIG(cborstream) +#if QT_CONFIG(cborstreamreader) // in qcborstream.cpp extern void qt_cbor_stream_set_error(QCborStreamReaderPrivate *d, QCborError error); +#endif +#if QT_CONFIG(cborstreamwriter) static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::EncodingOptions opt) { if (qt_is_nan(d)) { - if (opt & QCborValue::UseFloat16) { + if (opt & QCborValue::UseFloat) { +#ifndef QT_BOOTSTRAPPED if ((opt & QCborValue::UseFloat16) == QCborValue::UseFloat16) return writer.append(std::numeric_limits::quiet_NaN()); +#endif return writer.append(std::numeric_limits::quiet_NaN()); } return writer.append(qt_qnan()); @@ -866,15 +874,17 @@ static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::E } } - if (opt & QCborValue::UseFloat16) { + if (opt & QCborValue::UseFloat) { float f = float(d); if (f == d) { // no data loss, we could use float +#ifndef QT_BOOTSTRAPPED if ((opt & QCborValue::UseFloat16) == QCborValue::UseFloat16) { qfloat16 f16 = f; if (f16 == f) return writer.append(f16); } +#endif return writer.append(f); } @@ -882,7 +892,7 @@ static void writeDoubleToCbor(QCborStreamWriter &writer, double d, QCborValue::E writer.append(d); } -#endif // QT_CONFIG(cborstream) +#endif // QT_CONFIG(cborstreamwriter) static inline int typeOrder(Element e1, Element e2) { @@ -1305,7 +1315,7 @@ int QCborMap::compare(const QCborMap &other) const noexcept return compareContainer(d.data(), other.d.data()); } -#if QT_CONFIG(cborstream) +#if QT_CONFIG(cborstreamwriter) static void encodeToCbor(QCborStreamWriter &writer, const QCborContainerPrivate *d, qsizetype idx, QCborValue::EncodingOptions opt) { @@ -1393,7 +1403,9 @@ static void encodeToCbor(QCborStreamWriter &writer, const QCborContainerPrivate qWarning("QCborValue: found unknown type 0x%x", e.type); } } +#endif // QT_CONFIG(cborstreamwriter) +#if QT_CONFIG(cborstreamreader) static inline double integerOutOfRange(const QCborStreamReader &reader) { Q_ASSERT(reader.isInteger()); @@ -1650,7 +1662,7 @@ void QCborContainerPrivate::decodeFromCbor(QCborStreamReader &reader) if (reader.lastError() == QCborError::NoError) reader.leaveContainer(); } -#endif // QT_CONFIG(cborstream) +#endif // QT_CONFIG(cborstreamreader) /*! Creates a QCborValue with byte array value \a ba. The value can later be @@ -2350,7 +2362,7 @@ QCborValueRef QCborValue::operator[](qint64 key) return { container, index }; } -#if QT_CONFIG(cborstream) +#if QT_CONFIG(cborstreamreader) /*! Decodes one item from the CBOR stream found in \a reader and returns the equivalent representation. This function is recursive: if the item is a map @@ -2465,7 +2477,9 @@ QCborValue QCborValue::fromCbor(const QByteArray &ba, QCborParserError *error) overload of this function that accepts a QByteArray, also passing \a error, if provided. */ +#endif // QT_CONFIG(cborstreamreader) +#if QT_CONFIG(cborstreamwriter) /*! Encodes this QCborValue object to its CBOR representation, using the options specified in \a opt, and return the byte array containing that @@ -2588,7 +2602,7 @@ void QCborValueRef::toCbor(QCborStreamWriter &writer, QCborValue::EncodingOption { concrete().toCbor(writer, opt); } -#endif // QT_CONFIG(cborstream) +#endif // QT_CONFIG(cborstreamwriter) void QCborValueRef::assign(QCborValueRef that, const QCborValue &other) { diff --git a/src/corelib/serialization/qcborvalue.h b/src/corelib/serialization/qcborvalue.h index 071213e83a..1df8425d45 100644 --- a/src/corelib/serialization/qcborvalue.h +++ b/src/corelib/serialization/qcborvalue.h @@ -90,7 +90,9 @@ public: enum EncodingOption { SortKeysInMaps = 0x01, UseFloat = 0x02, +#ifndef QT_BOOTSTRAPPED UseFloat16 = UseFloat | 0x04, +#endif UseIntegers = 0x08, NoTransformation = 0 @@ -287,13 +289,15 @@ public: static QCborValue fromJsonValue(const QJsonValue &v); QJsonValue toJsonValue() const; -#if QT_CONFIG(cborstream) +#if QT_CONFIG(cborstreamreader) static QCborValue fromCbor(QCborStreamReader &reader); static QCborValue fromCbor(const QByteArray &ba, QCborParserError *error = nullptr); static QCborValue fromCbor(const char *data, qsizetype len, QCborParserError *error = nullptr) { return fromCbor(QByteArray(data, int(len)), error); } static QCborValue fromCbor(const quint8 *data, qsizetype len, QCborParserError *error = nullptr) { return fromCbor(QByteArray(reinterpret_cast(data), int(len)), error); } +#endif // QT_CONFIG(cborstreamreader) +#if QT_CONFIG(cborstreamwriter) QByteArray toCbor(EncodingOptions opt = NoTransformation); void toCbor(QCborStreamWriter &writer, EncodingOptions opt = NoTransformation); #endif @@ -441,7 +445,7 @@ public: QVariant toVariant() const { return concrete().toVariant(); } QJsonValue toJsonValue() const; -#if QT_CONFIG(cborstream) +#if QT_CONFIG(cborstreamwriter) QByteArray toCbor(QCborValue::EncodingOptions opt = QCborValue::NoTransformation) { return concrete().toCbor(opt); } void toCbor(QCborStreamWriter &writer, QCborValue::EncodingOptions opt = QCborValue::NoTransformation); diff --git a/src/corelib/serialization/serialization.pri b/src/corelib/serialization/serialization.pri index 7407e20d9e..ff653ca8f3 100644 --- a/src/corelib/serialization/serialization.pri +++ b/src/corelib/serialization/serialization.pri @@ -3,7 +3,9 @@ HEADERS += \ serialization/qcborarray.h \ serialization/qcborcommon.h \ + serialization/qcborcommon_p.h \ serialization/qcbormap.h \ + serialization/qcborstream.h \ serialization/qcborvalue.h \ serialization/qcborvalue_p.h \ serialization/qdatastream.h \ @@ -22,6 +24,7 @@ HEADERS += \ serialization/qxmlutils_p.h SOURCES += \ + serialization/qcborcommon.cpp \ serialization/qcbordiagnostic.cpp \ serialization/qcborvalue.cpp \ serialization/qdatastream.cpp \ @@ -36,12 +39,20 @@ SOURCES += \ serialization/qxmlstream.cpp \ serialization/qxmlutils.cpp -qtConfig(cborstream): { +qtConfig(cborstreamreader): { SOURCES += \ - serialization/qcborstream.cpp + serialization/qcborstreamreader.cpp HEADERS += \ - serialization/qcborstream.h + serialization/qcborstreamreader.h +} + +qtConfig(cborstreamwriter): { + SOURCES += \ + serialization/qcborstreamwriter.cpp + + HEADERS += \ + serialization/qcborstreamwriter.h } qtConfig(binaryjson): { diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 6230cc081d..bfd199a8ba 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -19,6 +19,10 @@ DEFINES += \ QT_NO_FOREACH \ QT_NO_CAST_FROM_ASCII +INCLUDEPATH += \ + $$PWD/.. \ + $$PWD/../../3rdparty/tinycbor/src + SOURCES += \ ../../corelib/codecs/qlatincodec.cpp \ ../../corelib/codecs/qtextcodec.cpp \ @@ -63,6 +67,8 @@ SOURCES += \ ../../corelib/kernel/qsharedmemory.cpp \ ../../corelib/kernel/qsystemsemaphore.cpp \ ../../corelib/plugin/quuid.cpp \ + ../../corelib/serialization/qcborcommon.cpp \ + ../../corelib/serialization/qcborstreamwriter.cpp \ ../../corelib/serialization/qcborvalue.cpp \ ../../corelib/serialization/qdatastream.cpp \ ../../corelib/serialization/qjsoncbor.cpp \