diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f09424..72ebe05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,8 +39,8 @@ else () set(PC_Requires "QtCore") endif () -option (QTGUI_TYPES "Build with support for QtGui types") -if (QTGUI_TYPES) +option (WITH_GUI_TYPES "Build with support for QtGui types") +if (WITH_GUI_TYPES) if (QT4_BUILD) find_package(Qt4 QTGUI) else () @@ -48,12 +48,17 @@ if (QTGUI_TYPES) endif () endif () -if (Qt5Gui_FOUND) - message("Qt5Gui found") - include_directories(${Qt5Gui_INCLUDE_DIRS}) - add_definitions(${Qt5Gui_DEFINITIONS}) +option (WITH_LOCATION_TYPES "Build with support for QtLocation types") +if (WITH_LOCATION_TYPES) + find_package(Qt5Location QUIET) +endif () + +if (Qt5Location_FOUND) + message("Qt5Location found") + include_directories(${Qt5Location_INCLUDE_DIRS}) + add_definitions(${Qt5Location_DEFINITIONS}) else () - message("Qt5Gui not found") + message("Qt5Location not found") endif () if (NOT WIN32) @@ -80,10 +85,10 @@ set(MSGPACK_QT_LIB_VERSION_STRING "${QMSGPACK_MAJOR}.${QMSGPACK_MINOR}.${QMSGPAC set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") add_subdirectory(src) -#if (MSGPACK_BUILD_TESTS) +if (BUILD_TESTS) enable_testing() add_subdirectory(tests) -#endif () +endif () install(EXPORT qmsgpack-export DESTINATION ${CMAKECONFIG_INSTALL_DIR} FILE MsgPackQtTargets.cmake) diff --git a/doc/basics.rst b/doc/basics.rst index 4981147..9dc7e80 100644 --- a/doc/basics.rst +++ b/doc/basics.rst @@ -73,6 +73,22 @@ Unpacking is handled by ``MsgPack::unpack()`` function: If packed data contains only one msgpack type (fixstr of fixmap for example), unpack will return it as ``QVariant(QString())`` and ``QVariant(QMap())`` respectively. But if there are several values packed, ``QVariant(QList())`` will be returned (consider this 5 bool values packed without msgpack's list: [0xc3, 0xc3, 0xc3, 0xc3, 0xc3]) +More types +========== + +There are built in packers and unpackers (basic and stream ones) for following types: +``QPoint, QSize, QRect, QTime, QDate, QDateTime, QColor, QGeoCoordinate``. +But since there is no such types in msgpack spec, ext type is used. + +Example: + +.. code-block:: cpp + + MsgPack::registerType(QMetaType::QPoint, 37); // 37 is msgpack user type id + QByteArray ba = MsgPack::pack(QPoint(12, 34)); + qDebug() << MsgPack::unpack(ba).toPoint(); + +Note, that QColor and QGeoCoordinate is enabled by default only in qmake project. Thread safety ============= diff --git a/doc/custom.rst b/doc/custom.rst new file mode 100644 index 0000000..a870643 --- /dev/null +++ b/doc/custom.rst @@ -0,0 +1,141 @@ +Custom types +------------ + +.. contents:: + :depth: 4 + +Custom QVariant +=============== + +You can provide two functions for packing QVariant with custom type inside to QByteArray and vise versa. +For example here is how QColor packer and unpacker looks like: + +.. code-block:: cpp + + QByteArray pack_qcolor(const QVariant &variant) + { + QColor color = variant.value(); + if (!color.isValid()) + return QByteArray(1, 0); + QByteArray data; + data.resize(4); + quint8 *p = (quint8 *)data.data(); + p[0] = color.red(); + p[1] = color.green(); + p[2] = color.blue(); + p[3] = color.alpha(); + return data; + } + + QVariant unpack_qcolor(const QByteArray &data) + { + if (data.length() == 1) + return QColor(); + quint8 *p = (quint8 *)data.data(); + return QColor(p[0], p[1], p[2], p[3]); + } + +And that's it! +Now register this two functions: + +.. code-block:: cpp + + MsgPack::registerPacker(QMetaType::QColor, 3, pack_qcolor); // 3 is msgpack ext type id + MsgPack::registerUnpacker(3, unpack_qcolor); + +After that ``MsgPack::pack(QColor(127, 127, 127))`` will start to work! + +Custom stream +============= + +You can provide stream operators for any other type you might want to work with. But there are some pitfalls to consider if you want to unpack something with other MsgPack implementations. + +Example: + +.. code-block:: cpp + + class SomeType + { + public: + double x() const { return m_x; } + void setX(double x) { m_x = x;} + + double y() const { return m_y; } + void setY(double y) { m_y = y;} + + double z() const { return m_z; } + void setZ(double z) { m_z = z;} + + private: + double m_x, m_y, m_z; + }; + + MsgPackStream &operator<<(MsgPackStream &s, const SomeType &t) + { + s.writeExtHeader(27, ); // size of packed double is 9 * 3 = 27 + s << t.x() << t.y() << t.z(); + return s; + } + + MsgPackStream &operator>>(MsgPackStream &s, SomeType &t) + { + quint32 len; + s.readExtHeader(len); + if (len != 27) { + s.setStatus(MsgPackStream::ReadCorruptData); + return s; + } + double x, y, z; + s >> x >> y >> z; + t.setX(x); + t.setY(y); + t.setZ(z); + return s; + } + +In this case size of data is known id advance, if this is not the case, then you can use QByteArray. Here is how QPoint operators are implemented: + +.. code-block:: cpp + + MsgPackStream &operator<<(MsgPackStream &s, const QPoint &point) + { + // we need to know user type id, that was registered with MsgPack::registerType + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QPoint); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); // stream inside stream ;) + if (point.isNull()) { // save some bytes if point is invalid + quint8 p[1] = {0}; + out.writeBytes((const char *)p, 1); + } else { + out << point.x() << point.y(); + } + s.writeExtHeader(ba.length(), msgpackType); // only now write msgpack ext field + s.writeBytes(ba.data(), ba.length()); // and variable length data + return s; + } + + MsgPackStream &operator>>(MsgPackStream &s, QPoint &point) + { + quint32 len; + s.readExtHeader(len); // read msgpack ext field + if (len == 1) { // handle invalid QPoint + point = QPoint(); + return s; + } + QByteArray ba; + ba.resize(len); + s.readBytes(ba.data(), len); // read len bytes to byte array + MsgPackStream in(ba); + int x, y; + in >> x >> y; + point = QPoint(x, y); + return s; + } + +.. tip:: + + Of course you can just stream out everything without any ext header and user type id's, like this: ``s << point.x() << point.y();`` but in that case you will not be able to unpack anything useful with MsgPack::unpack() or in other MsgPack implementations. \ No newline at end of file diff --git a/doc/index.rst b/doc/index.rst index 76b59df..3a05faf 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -6,6 +6,8 @@ qmsgpack - MessagePack serializer implementation for Qt install.rst basics.rst + stream.rst + custom.rst Contents: diff --git a/doc/install.rst b/doc/install.rst index 9dba323..cd753f4 100644 --- a/doc/install.rst +++ b/doc/install.rst @@ -5,7 +5,7 @@ Installation :depth: 4 qmsgpack is a pure Qt library (Qt4 and Qt5 supported), so you can build it for almost any platform Qt supports. -Also there are several build options: +There are two build methods: - CMake - qmake @@ -48,6 +48,14 @@ There are several useful cmake options available: Change build type to debug mode (default is `Release`), could be very useful if something goes wrong +.. cmdoption:: -DWITH_GUI_TYPES=True + + Build with support for QtGui types (QColor) + +.. cmdoption:: -DWITH_LOCATION_TYPES=True + + Build with support for QtLocation types(QGeoCoordinate). Might not work, because CMake seems to be failing to find QtLocation, in this case you can try qmake instead. + Add options before ``..`` as follow: .. code-block:: bash diff --git a/doc/stream.rst b/doc/stream.rst new file mode 100644 index 0000000..5fc0b39 --- /dev/null +++ b/doc/stream.rst @@ -0,0 +1,68 @@ +Streams +------- + +.. contents:: + :depth: 4 + +There are QDataStream analogue with almost identical API. Every basic type is supported as well as QList and Qt types (QPoint, QSize, QRect, QTime, QDate, QDateTime, QColor, QGeoCoordinate). More types are on the way. + +Packing +======= + +You can use any QIODevice derived class or QByteArray. + +.. code-block:: cpp + + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << 1 << true << "Hello"; + qDebug() << ba.toHex(); + +Of course you can unpack this byte array using ``MsgPack::unpack``: + +.. code-block:: cpp + + qDebug() << MsgPack::unpack(ba); + // output: + // QVariant(QVariantList, (QVariant(uint, 1), QVariant(bool, true), QVariant(QString, "Hello"))) + +QList of any type are also supported: + +.. code-block:: cpp + + QList list; + list << 1 << 2 << 3; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << list; + +Unpacking +========= + +To unpack QByteArray just pass it by value, or use QIODevice::ReadOnly for other devices: + +.. code-block:: cpp + + MsgPackStream in(ba); + QList list2; + in >> list2; + qDebug() << list2; // (1, 2, 3) + +More types +========== + +Include `` + // ... + + MsgPack::registerType(QMetaType::QPoint, 3); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << QPoint(1, 2) << QPoint(); + qDebug() << MsgPack::unpack(ba); + // output: + // QVariant(QVariantList, (QVariant(QPoint, QPoint(1,2)), QVariant(QPoint, QPoint(0,0)))) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cc4c4ba..0b4b8b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ -set(qmsgpack_srcs msgpack.cpp msgpackcommon.cpp msgpackstream.cpp private/pack_p.cpp private/unpack_p.cpp private/qt_types_p.cpp) +set(qmsgpack_srcs msgpack.cpp msgpackcommon.cpp msgpackstream.cpp private/pack_p.cpp private/unpack_p.cpp private/qt_types_p.cpp stream/time.cpp stream/geometry.cpp) set(qmsgpack_headers msgpack.h msgpackstream.h msgpackcommon.h msgpack_export.h endianhelper.h) +set(qmsgpack_stream_headers stream/location.h stream/time.h stream/geometry.h) add_library(qmsgpack SHARED ${qmsgpack_srcs} ${qmsgpack_headers}) @@ -31,4 +32,7 @@ install(TARGETS qmsgpack EXPORT qmsgpack-export LIBRARY DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin ARCHIVE DESTINATION ${LIB_INSTALL_DIR} - PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/qmsgpack) \ No newline at end of file + PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/qmsgpack) + + +install(FILES ${qmsgpack_stream_headers} DESTINATION ${INCLUDE_INSTALL_DIR}/qmsgpack/stream) \ No newline at end of file diff --git a/src/msgpack.cpp b/src/msgpack.cpp index fd04437..e18c361 100644 --- a/src/msgpack.cpp +++ b/src/msgpack.cpp @@ -38,6 +38,11 @@ bool MsgPack::registerUnpacker(qint8 msgpackType, MsgPack::unpack_user_f unpacke return MsgPackPrivate::register_unpacker(msgpackType, unpacker); } +qint8 MsgPack::msgpackType(int qType) +{ + return MsgPackPrivate::msgpack_type((QMetaType::Type)qType); +} + bool MsgPack::registerType(QMetaType::Type qType, quint8 msgpackType) { return MsgPackPrivate::register_qtype(qType, msgpackType); diff --git a/src/msgpack.h b/src/msgpack.h index 5052ab0..5a6d43e 100644 --- a/src/msgpack.h +++ b/src/msgpack.h @@ -14,6 +14,7 @@ namespace MsgPack MSGPACK_EXPORT QByteArray pack(const QVariant &variant); MSGPACK_EXPORT bool registerPacker(QMetaType::Type qType, qint8 msgpackType, pack_user_f packer); + MSGPACK_EXPORT qint8 msgpackType(int qType); MSGPACK_EXPORT bool registerType(QMetaType::Type qType, quint8 msgpackType); diff --git a/src/msgpackstream.cpp b/src/msgpackstream.cpp index 3ad134e..872d243 100644 --- a/src/msgpackstream.cpp +++ b/src/msgpackstream.cpp @@ -322,7 +322,47 @@ MsgPackStream &MsgPackStream::operator>>(QByteArray &array) bool MsgPackStream::readBytes(char *data, uint len) { CHECK_STREAM_PRECOND(false); - return dev->read(data, len) == len; + uint readed = dev->read(data, len); + if (readed != len) { + setStatus(ReadPastEnd); + return false; + } + return true; +} + +bool MsgPackStream::readExtHeader(quint32 &len) +{ + CHECK_STREAM_PRECOND(false); + quint8 d[6]; + if (dev->read((char*)d, 2) != 2) { + setStatus(ReadPastEnd); + return false; + } + if (d[0] >= MsgPack::FirstByte::FIXEXT1 && + d[0] <= MsgPack::FirstByte::FIXEX16) { + len = 1; + len <<= d[0] - MsgPack::FirstByte::FIXEXT1; + return true; + } + + quint8 toRead = 1; + toRead <<= d[0] - MsgPack::FirstByte::EXT8; + if (dev->read((char*)&d[2], toRead) != toRead) { + setStatus(ReadPastEnd); + return false; + } + + if (d[0] == MsgPack::FirstByte::EXT8) { + len = d[1]; + } else if (d[0] == MsgPack::FirstByte::EXT16) { + len = _msgpack_load16(quint32, &d[1]); + } else if (d[0] == MsgPack::FirstByte::EXT32) { + len = _msgpack_load32(quint32, &d[1]); + } else { + setStatus(ReadCorruptData); + return false; + } + return true; } MsgPackStream &MsgPackStream::operator<<(bool b) @@ -440,8 +480,49 @@ MsgPackStream &MsgPackStream::operator<<(QByteArray array) bool MsgPackStream::writeBytes(const char *data, uint len) { CHECK_STREAM_WRITE_PRECOND(false); - if (dev->write(data, len) != len) + if (dev->write(data, len) != len) { setStatus(WriteFailed); + return false; + } + return true; +} + +bool MsgPackStream::writeExtHeader(quint32 len, qint8 msgpackType) +{ + CHECK_STREAM_WRITE_PRECOND(false); + quint8 d[6]; + d[1] = msgpackType; + quint8 sz = 2; + if (len == 1) { + d[0] = 0xd4; + } else if (len == 2) { + d[0] = 0xd5; + } else if (len == 4) { + d[0] = 0xd6; + } else if (len == 8) { + d[0] = 0xd7; + } else if (len == 16) { + d[0] = 0xd8; + } else if (len <= std::numeric_limits::max()) { + d[0] = 0xc7; + d[1] = (quint8)len; + d[2] = msgpackType; + sz = 3; + } else if (len <= std::numeric_limits::max()) { + d[0] = 0xc8; + _msgpack_store16(&d[1], len); + d[3] = msgpackType; + sz = 4; + } else { + d[0] = 0xc9; + _msgpack_store32(&d[1], len); + d[5] = msgpackType; + sz = 6; + } + if (dev->write((const char *)d, sz) != sz) { + setStatus(WriteFailed); + return false; + } return true; } diff --git a/src/msgpackstream.h b/src/msgpackstream.h index e16e9a6..0e8aae7 100644 --- a/src/msgpackstream.h +++ b/src/msgpackstream.h @@ -40,6 +40,7 @@ public: MsgPackStream &operator>>(QString &str); MsgPackStream &operator>>(QByteArray &array); bool readBytes(char *data, uint len); + bool readExtHeader(quint32 &len); MsgPackStream &operator<<(bool b); MsgPackStream &operator<<(quint32 u32); @@ -52,6 +53,7 @@ public: MsgPackStream &operator<<(const char *str); MsgPackStream &operator<<(QByteArray array); bool writeBytes(const char *data, uint len); + bool writeExtHeader(quint32 len, qint8 msgpackType); private: QIODevice *dev; diff --git a/src/private/pack_p.cpp b/src/private/pack_p.cpp index 22a095a..eeb9712 100644 --- a/src/private/pack_p.cpp +++ b/src/private/pack_p.cpp @@ -47,6 +47,8 @@ quint8 *MsgPackPrivate::pack(const QVariant &v, quint8 *p, bool wr, QVector &user_data) { QMetaType::Type t = (QMetaType::Type)v.type() == QMetaType::User ? diff --git a/src/private/pack_p.h b/src/private/pack_p.h index 2b5daba..0fe8cce 100644 --- a/src/private/pack_p.h +++ b/src/private/pack_p.h @@ -1,3 +1,4 @@ + #ifndef PACK_P_H #define PACK_P_H @@ -19,6 +20,7 @@ typedef struct { qint8 type; } packer_t; bool register_packer(QMetaType::Type q_type, qint8 msgpack_type, MsgPack::pack_user_f packer); +qint8 msgpack_type(QMetaType::Type q_type); extern QHash user_packers; extern QReadWriteLock packers_lock; extern bool compatibilityMode; diff --git a/src/private/qt_types_p.cpp b/src/private/qt_types_p.cpp index 84735ce..3b70192 100644 --- a/src/private/qt_types_p.cpp +++ b/src/private/qt_types_p.cpp @@ -3,13 +3,19 @@ #include "unpack_p.h" #include "../msgpackstream.h" #include "../endianhelper.h" +#include "../stream/time.h" #include #ifdef QT_GUI_LIB #include +#else +#define NO_QTGUI_WARNING "qmsgpack was built without QtGui, hence some types are not available" +#endif + +#ifdef QT_LOCATION_LIB +#include #endif -#define NO_QTGUI_WARNING "Library built without QtGui, hence some types are not available" #include #include @@ -24,6 +30,13 @@ bool MsgPackPrivate::register_qtype(QMetaType::Type q_type, quint8 msgpack_type) qWarning() << NO_QTGUI_WARNING; return false; #endif //QT_GUI_LIB +#ifdef QT_LOCATION_LIB + } else if ((int)q_type == qMetaTypeId()) { + MsgPackPrivate::register_packer((QMetaType::Type)qMetaTypeId(), + msgpack_type, + pack_qgeocoordinate); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qgeocoordinate); +#endif } else if (q_type == QMetaType::QTime) { MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qtime); MsgPackPrivate::register_unpacker(msgpack_type, unpack_qtime); @@ -49,9 +62,11 @@ bool MsgPackPrivate::register_qtype(QMetaType::Type q_type, quint8 msgpack_type) #ifdef QT_GUI_LIB QByteArray MsgPackPrivate::pack_qcolor(const QVariant &variant) { + QColor color = variant.value(); + if (!color.isValid()) + return QByteArray(1, 0); QByteArray data; data.resize(4); - QColor color = variant.value(); quint8 *p = (quint8 *)data.data(); p[0] = color.red(); p[1] = color.green(); @@ -62,93 +77,83 @@ QByteArray MsgPackPrivate::pack_qcolor(const QVariant &variant) QVariant MsgPackPrivate::unpack_qcolor(const QByteArray &data) { + if (data.length() == 1) + return QColor(); quint8 *p = (quint8 *)data.data(); return QColor(p[0], p[1], p[2], p[3]); } #endif // QT_GUI_LIB -// Date and Time -void MsgPackPrivate::pack_qtime_raw(const QTime &time, quint8 *p) +#ifdef QT_LOCATION_LIB +QByteArray MsgPackPrivate::pack_qgeocoordinate(const QVariant &variant) { - quint8 hm, ms; - hm = (quint8)time.hour() << 4; - hm |= (quint8)time.minute() >> 2; - ms = ((quint8)time.minute() << 6) & 0xc0; // 11000000 - ms |= (quint8)time.second(); - p[0] = hm; p[1] = ms; - - if (time.msec() != 0) { - p[2] = (quint8)( (quint16)time.msec() >> 8 ); - p[3] = (quint8)time.msec(); - } + QGeoCoordinate c = variant.value(); + if (!c.isValid()) + return QByteArray(1, 0); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << c.latitude() << c.longitude(); + return ba; } -QTime MsgPackPrivate::unpack_qtime_raw(quint8 *p, bool with_ms) +QVariant MsgPackPrivate::unpack_qgeocoordinate(const QByteArray &data) { - quint8 h, m, s; - quint16 ms = 0; - h = p[0] >> 4; - m = (p[0] << 2) | (p[1] >> 6); - m &= 0x3f; // 00111111 - s = p[1] & 0x3f; - if (with_ms) - ms = (p[2] << 8) | p[3]; - return QTime(h, m, s, ms); + if (data.length() == 1) + return QVariant::fromValue(QGeoCoordinate()); + MsgPackStream in(data); + QGeoCoordinate c; + double x; + in >> x; + c.setLatitude(x); + in >> x; + c.setLongitude(x); + return QVariant::fromValue(c); } +#endif // QT_LOCATION_LIB QByteArray MsgPackPrivate::pack_qtime(const QVariant &variant) { QTime time = variant.toTime(); + if (!time.isValid()) + return QByteArray(1, 0); quint8 size = time.msec() == 0 ? 2 : 4; QByteArray data; data.resize(size); + data.data()[0] = 0; pack_qtime_raw(time, (quint8 *)data.data()); return data; } QVariant MsgPackPrivate::unpack_qtime(const QByteArray &data) { + if (data.size() == 1) + return QTime(); return unpack_qtime_raw((quint8 *)data.data(), data.size() == 4); } -void MsgPackPrivate::pack_qdate_raw(const QDate &date, quint8 *p) -{ - quint16 year = date.year(); - quint8 month = date.month(); - quint8 day = date.day(); - if (day > 15) - year |= 0x8000; - quint8 md = (month << 4) | (day & 0xf); - _msgpack_store16(p, year); - p[2] = md; -} - -QDate MsgPackPrivate::unpack_qdate_raw(quint8 *p) -{ - quint16 year = _msgpack_load16(quint16, p); - quint8 month = (p[2] & 0xf0) >> 4; - quint8 day = p[2] & 0xf; - day |= (quint8)((year & 0x8000) >> 11); - year &= 0x7fff; - return QDate(year, month, day); -} - QByteArray MsgPackPrivate::pack_qdate(const QVariant &variant) { + QDate date = variant.toDate(); + if (!date.isValid()) + return QByteArray(1, 0); QByteArray data; data.resize(3); - pack_qdate_raw(variant.toDate(), (quint8 *)data.data()); + pack_qdate_raw(date, (quint8 *)data.data()); return data; } QVariant MsgPackPrivate::unpack_qdate(const QByteArray &data) { + if (data.length() == 1) + return QDate(); return unpack_qdate_raw((quint8 *)data.data()); } QByteArray MsgPackPrivate::pack_qdatetime(const QVariant &variant) { QDateTime dt = variant.toDateTime(); + if (!dt.isValid()) + return QByteArray(1, 0); quint8 time_size = dt.time().msec() == 0 ? 2 : 4; QByteArray data; data.resize(3 + time_size); @@ -161,6 +166,8 @@ QByteArray MsgPackPrivate::pack_qdatetime(const QVariant &variant) QVariant MsgPackPrivate::unpack_qdatetime(const QByteArray &data) { + if (data.length() == 1) + return QDateTime(); quint8 *p = (quint8 *)data.data(); QDate d = unpack_qdate_raw(p); QTime t = unpack_qtime_raw(p + 3, data.size() == 7); @@ -170,33 +177,40 @@ QVariant MsgPackPrivate::unpack_qdatetime(const QByteArray &data) // Points and Vectors QByteArray MsgPackPrivate::pack_qpoint(const QVariant &variant) { + QPoint pt = variant.toPoint(); + if (pt.isNull()) + return QByteArray(1, 0); QByteArray packed; MsgPackStream stream(&packed, QIODevice::WriteOnly); - QPoint pt = variant.toPoint(); stream << pt.x() << pt.y(); return packed; } QVariant MsgPackPrivate::unpack_qpoint(const QByteArray &data) { + if (data.length() == 1) + return QPoint(); MsgPackStream stream(data); qint32 x, y; stream >> x >> y; - qDebug() << "unpack qpoint stream" << (stream.status() == MsgPackStream::Ok); return QPoint(x, y); } QByteArray MsgPackPrivate::pack_qsize(const QVariant &variant) { + QSize sz = variant.toSize(); + if (!sz.isValid()) + return QByteArray(1, 0); QByteArray packed; MsgPackStream stream(&packed, QIODevice::WriteOnly); - QSize sz = variant.toSize(); stream << sz.width() << sz.height(); return packed; } QVariant MsgPackPrivate::unpack_qsize(const QByteArray &data) { + if (data.length() == 1) + return QSize(); MsgPackStream stream(data); qint32 width, height; stream >> width >> height; @@ -206,6 +220,8 @@ QVariant MsgPackPrivate::unpack_qsize(const QByteArray &data) QByteArray MsgPackPrivate::pack_qrect(const QVariant &variant) { QRect rect = variant.toRect(); + if (!rect.isValid()) + return QByteArray(1, 0); QPoint pt1 = rect.topLeft(); QPoint pt2 = rect.bottomRight(); QByteArray packed; @@ -216,6 +232,8 @@ QByteArray MsgPackPrivate::pack_qrect(const QVariant &variant) QVariant MsgPackPrivate::unpack_qrect(const QByteArray &data) { + if (data.length() == 1) + return QRect(); MsgPackStream stream(data); qint32 x, y; stream >> x >> y; @@ -225,4 +243,3 @@ QVariant MsgPackPrivate::unpack_qrect(const QByteArray &data) rect.setBottomRight(QPoint(x, y)); return rect; } - diff --git a/src/private/qt_types_p.h b/src/private/qt_types_p.h index e0fbc9c..f09a794 100644 --- a/src/private/qt_types_p.h +++ b/src/private/qt_types_p.h @@ -13,6 +13,11 @@ QByteArray pack_qcolor(const QVariant &variant); QVariant unpack_qcolor(const QByteArray &data); #endif //QT_GUI_LIB +#ifdef QT_LOCATION_LIB +QByteArray pack_qgeocoordinate(const QVariant &variant); +QVariant unpack_qgeocoordinate(const QByteArray &data); +#endif + // Date and Time /** * @brief pack_qtime_raw internal: packs QTime to 4 or 2 bytes (with or without ms) @@ -20,14 +25,12 @@ QVariant unpack_qcolor(const QByteArray &data); * @param p pointer to preallocated array * format: (bits) hhhhmmmm mmssssss [------ms msmsmsms] */ -void pack_qtime_raw(const QTime &time, quint8 *p); // return 2 - without ms, 4 with ms /** * @brief unpack_qtime_raw internal: unpack 2 or 4 bytes to QTime * @param p data to unpack * @param with_ms true if array is 4 bytes (i.e. unpack with ms) * @return QTime */ -QTime unpack_qtime_raw(quint8 *p, bool with_ms); QByteArray pack_qtime(const QVariant &variant); QVariant unpack_qtime(const QByteArray &data); @@ -37,9 +40,7 @@ QVariant unpack_qtime(const QByteArray &data); * @param p pointer to preallocated array * format: (bits) d(5th bit)xyyyyyyyyyyyyyy, mmmmdddd */ -void pack_qdate_raw(const QDate &date, quint8 *p); -/// @brief internal: unpack bytes to QDate -QDate unpack_qdate_raw(quint8 *p); + QByteArray pack_qdate(const QVariant &variant); QVariant unpack_qdate(const QByteArray &data); diff --git a/src/src.pro b/src/src.pro index 23358fb..7a9de2e 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,5 +1,4 @@ -QT += core -QT -= gui +QT += core gui location TARGET = qmsgpack CONFIG -= app_bundle @@ -20,7 +19,10 @@ SOURCES += msgpack.cpp \ private/pack_p.cpp \ private/unpack_p.cpp \ private/qt_types_p.cpp \ - msgpackstream.cpp + msgpackstream.cpp \ + stream/time.cpp \ + stream/geometry.cpp \ + stream/location.cpp HEADERS += \ msgpack.h \ @@ -30,7 +32,10 @@ HEADERS += \ msgpackcommon.h \ msgpack_export.h \ private/qt_types_p.h \ - msgpackstream.h + msgpackstream.h \ + stream/location.h \ + stream/time.h \ + stream/geometry.h HEADERS_INSTALL = \ msgpack.h \ @@ -40,7 +45,8 @@ HEADERS_INSTALL = \ msgpackstream.h \ STREAM_HEADERS_INSTALL = \ - stream/location.h + stream/location.h \ + stream/time.h unix { header_files.files = $$HEADERS_INSTALL diff --git a/src/stream/geometry.cpp b/src/stream/geometry.cpp new file mode 100644 index 0000000..2754452 --- /dev/null +++ b/src/stream/geometry.cpp @@ -0,0 +1,117 @@ +#include "geometry.h" +#include "../msgpack.h" +#include "../msgpackstream.h" + +MsgPackStream &operator<<(MsgPackStream &s, const QPoint &point) +{ + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QPoint); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + if (point.isNull()) { + quint8 p[1] = {0}; + out.writeBytes((const char *)p, 1); + } else { + out << point.x() << point.y(); + } + s.writeExtHeader(ba.length(), msgpackType); + s.writeBytes(ba.data(), ba.length()); + return s; +} + +MsgPackStream &operator>>(MsgPackStream &s, QPoint &point) +{ + quint32 len; + s.readExtHeader(len); + if (len == 1) { + point = QPoint(); + return s; + } + QByteArray ba; + ba.resize(len); + s.readBytes(ba.data(), len); + MsgPackStream in(ba); + int x, y; + in >> x >> y; + point = QPoint(x, y); + return s; +} + +MsgPackStream &operator<<(MsgPackStream &s, const QSize &sz) +{ + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QSize); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + if (!sz.isValid()) { + quint8 p[1] = {0}; + out.writeBytes((const char *)p, 1); + } else { + out << sz.width() << sz.height(); + } + s.writeExtHeader(ba.length(), msgpackType); + s.writeBytes(ba.data(), ba.length()); + return s; +} + +MsgPackStream &operator>>(MsgPackStream &s, QSize &sz) +{ + quint32 len; + s.readExtHeader(len); + if (len == 1) { + sz = QSize(); + return s; + } + QByteArray ba; + ba.resize(len); + s.readBytes(ba.data(), len); + MsgPackStream in(ba); + int w, h; + in >> w >> h; + sz = QSize(w, h); + return s; +} + +MsgPackStream &operator<<(MsgPackStream &s, const QRect &rect) +{ + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QRect); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + if (!rect.isValid()) { + quint8 p[1] = {0}; + out.writeBytes((const char *)p, 1); + } else { + out << rect.left() << rect.top() << rect.width() << rect.height(); + } + s.writeExtHeader(ba.length(), msgpackType); + s.writeBytes(ba.data(), ba.length()); + return s; +} + +MsgPackStream &operator>>(MsgPackStream &s, QRect &rect) +{ + quint32 len; + s.readExtHeader(len); + if (len == 1) { + rect = QRect(); + return s; + } + QByteArray ba; + ba.resize(len); + s.readBytes(ba.data(), len); + MsgPackStream in(ba); + int l, t, w, h; + in >> l >> t >> w >> h; + rect = QRect(l, t, w, h); + return s; +} diff --git a/src/stream/geometry.h b/src/stream/geometry.h new file mode 100644 index 0000000..cea3302 --- /dev/null +++ b/src/stream/geometry.h @@ -0,0 +1,19 @@ +#ifndef GEOMETRY_H +#define GEOMETRY_H + +#include "../msgpack_export.h" +#include "../msgpackstream.h" + +#include + +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QPoint &point); +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QPoint &point); + +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QSize &sz); +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QSize &sz); + +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QRect &rect); +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QRect &rect); + +#endif // GEOMETRY_H + diff --git a/src/stream/location.cpp b/src/stream/location.cpp new file mode 100644 index 0000000..f47f25e --- /dev/null +++ b/src/stream/location.cpp @@ -0,0 +1,34 @@ +#ifdef QT_LOCATION_LIB + +#include "location.h" +#include "../msgpack.h" + +MsgPackStream& operator>>(MsgPackStream& s, QGeoCoordinate &coordinate) +{ + quint32 len; + s.readExtHeader(len); + if (len != 18) { + s.setStatus(MsgPackStream::ReadCorruptData); + return s; + } + double x; + s >> x; + coordinate.setLatitude(x); + s >> x; + coordinate.setLongitude(x); + return s; +} + +MsgPackStream &operator<<(MsgPackStream& s, const QGeoCoordinate &coordinate) +{ + qint8 msgpackType = MsgPack::msgpackType((QMetaType::Type)qMetaTypeId()); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + s.writeExtHeader(18, msgpackType); + s << coordinate.latitude() << coordinate.longitude(); + return s; +} + +#endif diff --git a/src/stream/location.h b/src/stream/location.h index df35df3..b824008 100644 --- a/src/stream/location.h +++ b/src/stream/location.h @@ -1,24 +1,15 @@ #ifndef QMSGPACK_STREAM_LOCATION_H #define QMSGPACK_STREAM_LOCATION_H +#ifdef QT_LOCATION_LIB + +#include "../msgpack_export.h" #include "../msgpackstream.h" #include -MsgPackStream& operator>>(MsgPackStream& s, QGeoCoordinate &coordinate) -{ - double x; - s >> x; - coordinate.setLatitude(x); - s >> x; - coordinate.setLongitude(x); - return s; -} +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QGeoCoordinate &coordinate); +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QGeoCoordinate &coordinate); -MsgPackStream &operator<<(MsgPackStream& s, const QGeoCoordinate &coordinate) -{ - s << coordinate.latitude() << coordinate.longitude(); - return s; -} - -#endif // QMSGPACK_STREAM_LOCATION_H \ No newline at end of file +#endif // QT_LOCATION_LIB +#endif // QMSGPACK_STREAM_LOCATION_H diff --git a/src/stream/time.cpp b/src/stream/time.cpp new file mode 100644 index 0000000..d45ac85 --- /dev/null +++ b/src/stream/time.cpp @@ -0,0 +1,166 @@ +#include "time.h" +#include "../msgpack.h" + +void MsgPackPrivate::pack_qtime_raw(const QTime &time, quint8 *p) +{ + quint8 hm, ms; + hm = (quint8)time.hour() << 4; + hm |= (quint8)time.minute() >> 2; + ms = ((quint8)time.minute() << 6) & 0xc0; // 11000000 + ms |= (quint8)time.second(); + p[0] = hm; p[1] = ms; + + if (time.msec() != 0) { + p[2] = (quint8)( (quint16)time.msec() >> 8 ); + p[3] = (quint8)time.msec(); + } +} + +QTime MsgPackPrivate::unpack_qtime_raw(quint8 *p, bool with_ms) +{ + quint8 h, m, s; + quint16 ms = 0; + h = p[0] >> 4; + m = (p[0] << 2) | (p[1] >> 6); + m &= 0x3f; // 00111111 + s = p[1] & 0x3f; + if (with_ms) + ms = (p[2] << 8) | p[3]; + return QTime(h, m, s, ms); +} + +void MsgPackPrivate::pack_qdate_raw(const QDate &date, quint8 *p) +{ + quint16 year = date.year(); + quint8 month = date.month(); + quint8 day = date.day(); + if (day > 15) + year |= 0x8000; + quint8 md = (month << 4) | (day & 0xf); + _msgpack_store16(p, year); + p[2] = md; +} + +QDate MsgPackPrivate::unpack_qdate_raw(quint8 *p) +{ + quint16 year = _msgpack_load16(quint16, p); + quint8 month = (p[2] & 0xf0) >> 4; + quint8 day = p[2] & 0xf; + day |= (quint8)((year & 0x8000) >> 11); + year &= 0x7fff; + return QDate(year, month, day); +} + +MsgPackStream &operator<<(MsgPackStream &s, const QTime &t) +{ + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QTime); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + quint8 len = 1; + if (t.isValid()) + len = t.msec() == 0 ? 2 : 4; + s.writeExtHeader(len, msgpackType); + quint8 p[4] = {0}; + if (len != 1) + MsgPackPrivate::pack_qtime_raw(t, p); + s.writeBytes((const char *)p, len); + return s; +} + +MsgPackStream& operator>>(MsgPackStream& s, QTime &t) +{ + quint32 len; + s.readExtHeader(len); + if (len != 1 && len != 2 && len != 4) { + s.setStatus(MsgPackStream::ReadCorruptData); + t = QTime(); + return s; + } + if (len == 1) { + t == QTime(); + return s; + } + quint8 p[4]; + s.readBytes((char *)p, len); + t = MsgPackPrivate::unpack_qtime_raw(p, len == 4); + return s; +} + +MsgPackStream &operator<<(MsgPackStream &s, const QDate &date) +{ + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QDate); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + quint8 len = date.isValid() ? 3 : 1; + s.writeExtHeader(len, msgpackType); + quint8 p[3] = {0}; + if (len != 1) + MsgPackPrivate::pack_qdate_raw(date, p); + s.writeBytes((const char *)p, len); + return s; +} + +MsgPackStream& operator>>(MsgPackStream& s, QDate &date) +{ + quint32 len; + s.readExtHeader(len); + if (len != 1 && len != 3) { + s.setStatus(MsgPackStream::ReadCorruptData); + date = QDate(); + return s; + } + if (len == 1) { + date = QDate(); + return s; + } + quint8 p[3]; + s.readBytes((char *)p, len); + date = MsgPackPrivate::unpack_qdate_raw(p); + return s; +} + +MsgPackStream &operator<<(MsgPackStream& s, const QDateTime &dt) +{ + qint8 msgpackType = MsgPack::msgpackType(QMetaType::QDateTime); + if (msgpackType == -1) { + s.setStatus(MsgPackStream::WriteFailed); + return s; + } + quint8 len = 1; + if (dt.isValid()) { + len = 3; + len += dt.time().msec() == 0 ? 2 : 4; + } + s.writeExtHeader(len, msgpackType); + quint8 p[7] = {0}; + if (len != 1) { + MsgPackPrivate::pack_qdate_raw(dt.date(), p); + MsgPackPrivate::pack_qtime_raw(dt.time(), p + 3); + } + s.writeBytes((const char *)p, len); + return s; +} + +MsgPackStream& operator>>(MsgPackStream& s, QDateTime &dt) +{ + quint32 len; + s.readExtHeader(len); + if (len != 1 && len != 5 && len != 7) { + s.setStatus(MsgPackStream::ReadCorruptData); + dt = QDateTime(); + return s; + } + if (len == 1) { + dt = QDateTime(); + return s; + } + quint8 p[7]; + s.readBytes((char *)p, len); + dt.setDate(MsgPackPrivate::unpack_qdate_raw(p)); + dt.setTime(MsgPackPrivate::unpack_qtime_raw(p + 3, len == 7)); + return s; +} diff --git a/src/stream/time.h b/src/stream/time.h new file mode 100644 index 0000000..82728cb --- /dev/null +++ b/src/stream/time.h @@ -0,0 +1,26 @@ +#ifndef TIME_H +#define TIME_H + +#include "../msgpack_export.h" +#include "../msgpackstream.h" + +#include + +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QTime &time); +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QTime &time); + +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QDate &date); +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QDate &date); + +MSGPACK_EXPORT MsgPackStream &operator<<(MsgPackStream& s, const QDateTime &dt); +MSGPACK_EXPORT MsgPackStream& operator>>(MsgPackStream& s, QDateTime &dt); + +namespace MsgPackPrivate { +void pack_qtime_raw(const QTime &time, quint8 *p); +QTime unpack_qtime_raw(quint8 *p, bool with_ms); +void pack_qdate_raw(const QDate &date, quint8 *p); +QDate unpack_qdate_raw(quint8 *p); +} + +#endif // TIME_H + diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ae96079..e600ac9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,7 +8,7 @@ if (Qt5Core_FOUND) set(TEST_LIBRARIES ${Qt5Test_LIBRARIES}) endif () -set(TEST_SUBDIRS pack unpack mixed stream) +set(TEST_SUBDIRS pack unpack mixed stream qttypes) foreach(subdir ${TEST_SUBDIRS}) add_subdirectory(${subdir}) diff --git a/tests/qttypes/qttypes_test.cpp b/tests/qttypes/qttypes_test.cpp index 287a233..03468eb 100644 --- a/tests/qttypes/qttypes_test.cpp +++ b/tests/qttypes/qttypes_test.cpp @@ -4,18 +4,63 @@ #include #include +#ifdef QT_GUI_LIB +#include +#endif + +#ifdef QT_LOCATION_LIB +#include +#endif + class QtTypesTest : public QObject { Q_OBJECT private Q_SLOTS: +#ifdef QT_GUI_LIB + void test_qcolor(); +#endif +#ifdef QT_LOCATION_LIB + void test_qgeocoordinte(); +#endif void test_qtime(); void test_qdate(); + void test_qdatetime(); void test_qpoint(); void test_qsize(); void test_qrect(); }; +#ifdef QT_GUI_LIB +void QtTypesTest::test_qcolor() +{ + MsgPack::registerType(QMetaType::QColor, 0x75); + QColor color; + QByteArray packed = MsgPack::pack(color); + QColor color2 = MsgPack::unpack(packed).value(); + QVERIFY(color == color2); + QVERIFY(packed.size() == 3); + + color = QColor(100, 200, 255); + packed = MsgPack::pack(color); + color2 = MsgPack::unpack(packed).value(); + QVERIFY(color == color2); + QVERIFY(packed.size() == 6); +} +#endif + +#ifdef QT_LOCATION_LIB +void QtTypesTest::test_qgeocoordinte() +{ + MsgPack::registerType((QMetaType::Type)qMetaTypeId(), 0x76); + QGeoCoordinate c; + QByteArray packed = MsgPack::pack(QVariant::fromValue(c)); + QGeoCoordinate c2 = MsgPack::unpack(packed).value(); + QVERIFY(c == c2); + QVERIFY(packed.size() == 3); +} +#endif + void QtTypesTest::test_qtime() { MsgPack::registerType(QMetaType::QTime, 0x77); @@ -23,7 +68,7 @@ void QtTypesTest::test_qtime() QByteArray packed = MsgPack::pack(t); QTime t2 = MsgPack::unpack(packed).toTime(); QVERIFY(t == t2); - QVERIFY(packed.size() == 1); + QVERIFY(packed.size() == 3); t = QTime(12, 01, 01, 0); packed = MsgPack::pack(t); @@ -51,7 +96,7 @@ void QtTypesTest::test_qdate() QByteArray packed = MsgPack::pack(d); QDate d2 = MsgPack::unpack(packed).toDate(); QVERIFY(d == d2); - QVERIFY(packed.size() == 1); + QVERIFY(packed.size() == 3); d = QDate(1234, 12, 1); packed = MsgPack::pack(d); @@ -64,13 +109,35 @@ void QtTypesTest::test_qdate() QVERIFY(d == d2); } +void QtTypesTest::test_qdatetime() +{ + MsgPack::registerType(QMetaType::QDateTime, 3); + QDateTime dt; + QByteArray packed = MsgPack::pack(dt); + QVERIFY(packed.size() == 3); + QDateTime dt2 = MsgPack::unpack(packed).toDateTime(); + QVERIFY(dt == dt2); + + dt = QDateTime(QDate(1234, 12, 1), QTime(12, 34, 56)); + packed = MsgPack::pack(dt); + QVERIFY(packed.size() == 8); + dt2 = MsgPack::unpack(packed).toDateTime(); + QVERIFY(dt == dt2); + + dt = QDateTime(QDate(1234, 12, 1), QTime(12, 34, 56, 789)); + packed = MsgPack::pack(dt); + QVERIFY(packed.size() == 10); + dt2 = MsgPack::unpack(packed).toDateTime(); + QVERIFY(dt == dt2); +} + void QtTypesTest::test_qpoint() { MsgPack::registerType(QMetaType::QPoint, 0x79); QPoint pt; QByteArray packed = MsgPack::pack(pt); - QVERIFY(packed.size() == 1); + QVERIFY(packed.size() == 3); QPoint pt2 = MsgPack::unpack(packed).toPoint(); QVERIFY(pt == pt2); @@ -99,7 +166,7 @@ void QtTypesTest::test_qsize() QSize sz; QByteArray packed = MsgPack::pack(sz); -// QVERIFY(packed.size() == 1); + QVERIFY(packed.size() == 3); QSize sz2 = MsgPack::unpack(packed).toSize(); QVERIFY(sz == sz2); @@ -121,7 +188,7 @@ void QtTypesTest::test_qrect() MsgPack::registerType(QMetaType::QRect, 81); QRect r; QByteArray packed = MsgPack::pack(r); - QVERIFY(packed.size() == 1); + QVERIFY(packed.size() == 3); QRect r2 = MsgPack::unpack(packed).toRect(); QVERIFY(r == r2); diff --git a/tests/stream/stream_test.cpp b/tests/stream/stream_test.cpp index ed0041b..cb917d9 100644 --- a/tests/stream/stream_test.cpp +++ b/tests/stream/stream_test.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include class StreamTest : public QObject @@ -18,6 +20,9 @@ private Q_SLOTS: void test_double(); void test_bin(); void test_array(); + void test_ext(); + void test_time(); + void test_geometry(); }; void StreamTest::test_unpack_integers() @@ -367,5 +372,357 @@ void StreamTest::test_array() } } +void StreamTest::test_ext() +{ + { + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out.writeExtHeader(1, 7); + QVERIFY(out.status() == MsgPackStream::Ok); + quint8 *p = (quint8 *)ba.data(); + QVERIFY(ba.length() == 2); + QVERIFY(p[0] == 0xd4); + QVERIFY(p[1] == 7); + + MsgPackStream in(ba); + quint32 len; + QVERIFY(in.readExtHeader(len)); + QVERIFY(in.status() == MsgPackStream::Ok); + QVERIFY(len == 1); + QVERIFY(in.device()->pos() == 2); + } + { + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out.writeExtHeader(17, 7); + QVERIFY(out.status() == MsgPackStream::Ok); + quint8 *p = (quint8 *)ba.data(); + QVERIFY(ba.length() == 3); + QVERIFY(p[0] == 0xc7); + QVERIFY(p[2] == 7); + + MsgPackStream in(ba); + quint32 len; + QVERIFY(in.readExtHeader(len)); + QVERIFY(in.status() == MsgPackStream::Ok); + QVERIFY(len == 17); + QVERIFY(in.device()->pos() == 3); + } + { + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out.writeExtHeader(256, 7); + QVERIFY(out.status() == MsgPackStream::Ok); + quint8 *p = (quint8 *)ba.data(); + QVERIFY(ba.length() == 4); + QVERIFY(p[0] == 0xc8); + QVERIFY(p[3] == 7); + + MsgPackStream in(ba); + quint32 len; + QVERIFY(in.readExtHeader(len)); + QVERIFY(in.status() == MsgPackStream::Ok); + QVERIFY(len == 256); + QVERIFY(in.device()->pos() == 4); + } + { + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out.writeExtHeader(65536, 7); + QVERIFY(out.status() == MsgPackStream::Ok); + quint8 *p = (quint8 *)ba.data(); + QVERIFY(ba.length() == 6); + QVERIFY(p[0] == 0xc9); + QVERIFY(p[5] == 7); + + MsgPackStream in(ba); + quint32 len; + QVERIFY(in.readExtHeader(len)); + QVERIFY(in.status() == MsgPackStream::Ok); + QVERIFY(len == 65536); + QVERIFY(in.device()->pos() == 6); + } +} + +void StreamTest::test_time() +{ + MsgPack::registerType(QMetaType::QTime, 3); + MsgPack::registerType(QMetaType::QDate, 4); + MsgPack::registerType(QMetaType::QDateTime, 5); + { + QTime t; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << t; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 3); + + MsgPackStream in(ba); + QTime t2; + in >> t2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(t == t2); + } + { + QTime t = QTime(12, 01, 01, 0); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << t; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 4); + + MsgPackStream in(ba); + QTime t2; + in >> t2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(t == t2); + } + { + QTime t = QTime(12, 59, 59, 0); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << t; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 4); + + MsgPackStream in(ba); + QTime t2; + in >> t2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(t == t2); + } + { + QTime t = QTime(12, 34, 56, 789); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << t; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 6); + + MsgPackStream in(ba); + QTime t2; + in >> t2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(t == t2); + } + { + QDate d; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << d; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 3); + + MsgPackStream in(ba); + QDate d2; + in >> d2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(d == d2); + } + { + QDate d = QDate(1234, 12, 1); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << d; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 6); + + MsgPackStream in(ba); + QDate d2; + in >> d2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(d == d2); + } + { + QDate d = QDate(9999, 1, 31); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << d; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 6); + + MsgPackStream in(ba); + QDate d2; + in >> d2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(d == d2); + } + { + QDateTime dt; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << dt; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 3); + + MsgPackStream in(ba); + QDateTime dt2; + in >> dt2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(dt == dt2); + } + { + QDateTime dt(QDate(1234, 12, 1), QTime(12, 34, 56)); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << dt; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 8); + + MsgPackStream in(ba); + QDateTime dt2; + in >> dt2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(dt == dt2); + } + { + QDateTime dt(QDate(1234, 12, 1), QTime(12, 34, 56, 789)); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << dt; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 10); + + MsgPackStream in(ba); + QDateTime dt2; + in >> dt2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(dt == dt2); + } +} + +void StreamTest::test_geometry() +{ + MsgPack::registerType(QMetaType::QPoint, 6); + MsgPack::registerType(QMetaType::QSize, 7); + MsgPack::registerType(QMetaType::QRect, 8); + { + QPoint pt; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << pt; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 3); + + MsgPackStream in(ba); + QPoint pt2; + in >> pt2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(pt == pt2); + } + { + QPoint pt(1, 2); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << pt; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 4); + + MsgPackStream in(ba); + QPoint pt2; + in >> pt2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(pt == pt2); + } + { + QPoint pt(1234, 5678); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << pt; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 9); + + MsgPackStream in(ba); + QPoint pt2; + in >> pt2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(pt == pt2); + } + { + QSize sz; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << sz; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 3); + + MsgPackStream in(ba); + QSize sz2; + in >> sz2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(sz == sz2); + } + { + QSize sz(1, 2); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << sz; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 4); + + MsgPackStream in(ba); + QSize sz2; + in >> sz2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(sz == sz2); + } + { + QSize sz(1234, 5678); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << sz; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 9); + + MsgPackStream in(ba); + QSize sz2; + in >> sz2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(sz == sz2); + } + { + QRect rect; + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << rect; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 3); + + MsgPackStream in(ba); + QRect rect2; + in >> rect2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(rect == rect2); + } + { + QRect rect(1, 2, 3, 4); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << rect; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 6); + + MsgPackStream in(ba); + QRect rect2; + in >> rect2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(rect == rect2); + } + { + QRect rect(0, 0, 65536, 65536); + QByteArray ba; + MsgPackStream out(&ba, QIODevice::WriteOnly); + out << rect; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(ba.length() == 15); + + MsgPackStream in(ba); + QRect rect2; + in >> rect2; + QVERIFY(out.status() == MsgPackStream::Ok); + QVERIFY(rect == rect2); + } +} + QTEST_APPLESS_MAIN(StreamTest) #include "stream_test.moc"