diff --git a/.gitignore b/.gitignore index 33c5afb..3e5bad7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ -*.pro.user +*.pro.user* build lib -Makefile +Makefile* *.autosave +bin/ +*.o diff --git a/.travis.yml b/.travis.yml index b37d3ee..ac76a5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ before_install: script: - mkdir build - cd build - - cmake .. + - if [ "$QT4_BUILD" == "OFF" ]; then cmake -DBUILD_TESTS=TRUE -DQT4_BUILD=FALSE ..; else cmake -DBUILD_TESTS=TRUE -DQT4_BUILD=TRUE ..; fi - make + - export CTEST_OUTPUT_ON_FAILURE=1 - make test diff --git a/CMakeLists.txt b/CMakeLists.txt index 307146b..5791159 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,6 +39,23 @@ else () set(PC_Requires "QtCore") endif () +option (QTGUI_TYPES "Build with support for QtGui types") +if (QTGUI_TYPES) + if (QT4_BUILD) + find_package(Qt4 QTGUI) + else () + find_package(Qt5Gui QUIET) + endif () +endif () + +if (Qt5Gui_FOUND) + message("Qt5Gui found") + include_directories(${Qt5Gui_INCLUDE_DIRS}) + add_definitions(${Qt5Gui_DEFINITIONS}) +else () + message("Qt5Gui not found") +endif () + if (NOT WIN32) set(QT_DONT_USE_QTGUI TRUE) endif () @@ -63,14 +80,14 @@ 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) file(RELATIVE_PATH relInstallDir ${CMAKE_INSTALL_PREFIX}/$CMAKECONFIG_INSTALL_DIR} ${CMAKE_INSTALL_PREFIX}) add_custom_target(uninstall - "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") \ No newline at end of file + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e82a68b..6c76121 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ -set(qmsgpack_srcs msgpack.cpp msgpack_common.cpp private/pack_p.cpp private/unpack_p.cpp) -set(qmsgpack_headers msgpack.h msgpack_common.h msgpack_export.h) +set(qmsgpack_srcs msgpack.cpp msgpack_common.cpp msgpackstream.cpp private/pack_p.cpp private/unpack_p.cpp private/qt_types_p.cpp) +set(qmsgpack_headers msgpack.h msgpackstream.h msgpack_common.h msgpack_export.h endianhelper.h) add_library(qmsgpack SHARED ${qmsgpack_srcs} ${qmsgpack_headers}) @@ -9,6 +9,10 @@ else () target_link_libraries(qmsgpack ${QT_LIBRARIES}) endif () +if (Qt5Gui_FOUND) + target_link_libraries(qmsgpack Qt5::Gui) +endif () + configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/msgpack_common.h.in" "${CMAKE_CURRENT_SOURCE_DIR}/msgpack_common.h" @@ -27,4 +31,4 @@ 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) diff --git a/src/CMakeLists.txt~ b/src/CMakeLists.txt~ new file mode 100644 index 0000000..3d656d3 --- /dev/null +++ b/src/CMakeLists.txt~ @@ -0,0 +1,34 @@ +set(qmsgpack_srcs msgpack.cpp msgpack_common.cpp stream.cpp private/pack_p.cpp private/unpack_p.cpp private/qt_types_p.cpp) +set(qmsgpack_headers msgpack.h stream.h msgpack_common.h msgpack_export.h endianhelper.h) + +add_library(qmsgpack SHARED ${qmsgpack_srcs} ${qmsgpack_headers}) + +if (Qt5Core_FOUND) + target_link_libraries(qmsgpack Qt5::Core) +else () + target_link_libraries(qmsgpack ${QT_LIBRARIES}) +endif () + +if (Qt5Gui_FOUND) + target_link_libraries(qmsgpack Qt5::Gui) +endif () + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/msgpack_common.h.in" + "${CMAKE_CURRENT_SOURCE_DIR}/msgpack_common.h" + IMMEDIATE @ONLY) + +if (NOT android) + set_target_properties(qmsgpack PROPERTIES + VERSION ${QMSGPACK_MAJOR}.${QMSGPACK_MINOR}.${QMSGPACK_VERSION} + SOVERSION ${QMSGPACK_MAJOR}) +endif () +set_target_properties(qmsgpack PROPERTIES + DEFINE_SYMBOL MSGPACK_MAKE_LIB + PUBLIC_HEADER "${qmsgpack_headers}") + +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 diff --git a/src/private/sysdep.h b/src/endianhelper.h similarity index 100% rename from src/private/sysdep.h rename to src/endianhelper.h diff --git a/src/msgpack-qt.pro b/src/msgpack-qt.pro index f1c6d18..d69ffaf 100644 --- a/src/msgpack-qt.pro +++ b/src/msgpack-qt.pro @@ -6,7 +6,7 @@ QT += core -QT -= gui +QT += gui TARGET = qmsgpack CONFIG -= app_bundle @@ -26,7 +26,9 @@ CONFIG(debug, debug|release) { SOURCES += msgpack.cpp \ msgpack_common.cpp \ private/pack_p.cpp \ - private/unpack_p.cpp + private/unpack_p.cpp \ + private/qt_types_p.cpp \ + stream.cpp HEADERS += \ msgpack.h \ @@ -34,4 +36,6 @@ HEADERS += \ private/unpack_p.h \ private/sysdep.h \ msgpack_common.h \ - msgpack_export.h + msgpack_export.h \ + private/qt_types_p.h \ + stream.h diff --git a/src/msgpack.cpp b/src/msgpack.cpp index 36e011e..57aefd1 100644 --- a/src/msgpack.cpp +++ b/src/msgpack.cpp @@ -2,6 +2,8 @@ #include "msgpack_common.h" #include "private/unpack_p.h" #include "private/pack_p.h" +#include "private/qt_types_p.h" +#include QVariant MsgPack::unpack(const QByteArray &data) { @@ -14,13 +16,14 @@ QVariant MsgPack::unpack(const QByteArray &data) QByteArray MsgPack::pack(const QVariant &variant) { quint8 *p = 0; - quint8 *end = MsgPackPrivate::pack(variant, p, false); + QVector user_data; + quint8 *end = MsgPackPrivate::pack(variant, p, false, user_data); quint32 size = end - p; //qDebug() << "size probe:" << size; QByteArray arr; arr.resize(size); - end = MsgPackPrivate::pack(variant, (quint8 *)arr.data(), true); + end = MsgPackPrivate::pack(variant, (quint8 *)arr.data(), true, user_data); return arr; } @@ -35,6 +38,11 @@ bool MsgPack::registerUnpacker(qint8 msgpackType, MsgPack::unpack_user_f unpacke return MsgPackPrivate::register_unpacker(msgpackType, unpacker); } +bool MsgPack::registerType(QMetaType::Type qType, quint8 msgpackType) +{ + return MsgPackPrivate::register_qtype(qType, msgpackType); +} + void MsgPack::setCompatibilityModeEnabled(bool enabled) { MsgPackPrivate::compatibilityMode = enabled; diff --git a/src/msgpack.h b/src/msgpack.h index cf4c540..e90cc44 100644 --- a/src/msgpack.h +++ b/src/msgpack.h @@ -5,7 +5,7 @@ #include "msgpack_export.h" #include -#include +#include namespace MsgPack { @@ -15,7 +15,22 @@ namespace MsgPack MSGPACK_EXPORT QByteArray pack(const QVariant &variant); MSGPACK_EXPORT bool registerPacker(QMetaType::Type qType, qint8 msgpackType, pack_user_f packer); + MSGPACK_EXPORT bool registerType(QMetaType::Type qType, quint8 msgpackType); + MSGPACK_EXPORT void setCompatibilityModeEnabled(bool enabled); -} + + namespace ExtHelpers { + quint8 * unpack_upto_quint8(quint8 *to, quint8 *from, bool *success); + quint8 * unpack_upto_quint16(quint16 *to, quint8 *from, bool *success); + quint8 * unpack_upto_quint32(quint32 *to, quint8 *from, bool *success); + quint8 * unpack_upto_quint64(quint64 *to, quint8 *from, bool *success); + quint8 * unpack_upto_qint8(qint8 *to, quint8 *from, bool *success); + quint8 * unpack_upto_qint16(qint16 *to, quint8 *from, bool *success); + quint8 * unpack_upto_qint32(qint32 *to, quint8 *from, bool *success); + quint8 * unpack_upto_qint64(qint64 *to, quint8 *from, bool *success); + quint8 * unpack_float(float *to, quint8 *from, bool *success); + quint8 * unpack_double(double *to, quint8 *from, bool *success); + } // ExtHelpers +} // MsgPack #endif // MSGPACK_H diff --git a/src/msgpack_common.h b/src/msgpack_common.h index 3c62f7c..68a6479 100644 --- a/src/msgpack_common.h +++ b/src/msgpack_common.h @@ -2,6 +2,7 @@ #define COMMON_H #include +#include #define MSGPACK_MAJOR 0 #define MSGPACK_MINOR 1 @@ -9,14 +10,14 @@ namespace MsgPack { /** - * pack some variant to byte array data - * when write == false only calculates and returns size - * when write == true writes bytes to data, and returns the same size - * return type size + * @brief pack user type to byte array data + * @arg variant user type, must be registered first + * @return array with user data only, all other fields will be added automatically */ -typedef quint32 (*pack_user_f)(const QVariant &variant, QByteArray &data, bool write); +typedef QByteArray (*pack_user_f)(const QVariant &variant); /** - * unpack user type to QVariant + * @brief unpack user type to QVariant + * @arg data only user data, without size and messagepack type */ typedef QVariant (*unpack_user_f)(const QByteArray &data); /** @@ -24,6 +25,48 @@ typedef QVariant (*unpack_user_f)(const QByteArray &data); * @return current version */ QString version(); +/** + * @brief The FirstByte enum + * From Message Pack spec + */ +namespace FirstByte { +const quint8 POSITIVE_FIXINT = 0x7f; +const quint8 FIXMAP = 0x80; +const quint8 FIXARRAY = 0x90; +const quint8 FIXSTR = 0xa0; +const quint8 NIL = 0xc0; +const quint8 NEVER_USED = 0xc1; +const quint8 MFALSE = 0xc2; +const quint8 MTRUE = 0xc3; +const quint8 BIN8 = 0xc4; +const quint8 BIN16 = 0xc5; +const quint8 BIN32 = 0xc6; +const quint8 EXT8 = 0xc7; +const quint8 EXT16 = 0xc8; +const quint8 EXT32 = 0xc9; +const quint8 FLOAT32 = 0xca; +const quint8 FLOAT64 = 0xcb; +const quint8 UINT8 = 0xcc; +const quint8 UINT16 = 0xcd; +const quint8 UINT32 = 0xce; +const quint8 UINT64 = 0xcf; +const quint8 INT8 = 0xd0; +const quint8 INT16 = 0xd1; +const quint8 INT32 = 0xd2; +const quint8 INT64 = 0xd3; +const quint8 FIXEXT1 = 0xd4; +const quint8 FIXEXT2 = 0xd5; +const quint8 FIXEXT4 = 0xd6; +const quint8 FIXEXT8 = 0xd7; +const quint8 FIXEX16 = 0xd8; +const quint8 STR8 = 0xd9; +const quint8 STR16 = 0xda; +const quint8 STR32 = 0xdb; +const quint8 ARRAY16 = 0xdc; +const quint8 ARRAY32 = 0xdd; +const quint8 MAP16 = 0xde; +const quint8 MAP32 = 0xdf; +const quint8 NEGATIVE_FIXINT = 0xe0; +} } - #endif // COMMON_H diff --git a/src/msgpack_common.h.in b/src/msgpack_common.h.in index 9e140bc..aaf6d82 100644 --- a/src/msgpack_common.h.in +++ b/src/msgpack_common.h.in @@ -2,6 +2,7 @@ #define COMMON_H #include +#include #define MSGPACK_MAJOR @QMSGPACK_MAJOR@ #define MSGPACK_MINOR @QMSGPACK_MINOR@ @@ -9,14 +10,14 @@ namespace MsgPack { /** - * pack some variant to byte array data - * when write == false only calculates and returns size - * when write == true writes bytes to data, and returns the same size - * return type size + * @brief pack user type to byte array data + * @arg variant user type, must be registered first + * @return array with user data only, all other fields will be added automatically */ -typedef quint32 (*pack_user_f)(const QVariant &variant, QByteArray &data, bool write); +typedef QByteArray (*pack_user_f)(const QVariant &variant); /** - * unpack user type to QVariant + * @brief unpack user type to QVariant + * @arg data only user data, without size and messagepack type */ typedef QVariant (*unpack_user_f)(const QByteArray &data); /** @@ -24,6 +25,48 @@ typedef QVariant (*unpack_user_f)(const QByteArray &data); * @return current version */ QString version(); +/** + * @brief The FirstByte enum + * From Message Pack spec + */ +namespace FirstByte { +const quint8 POSITIVE_FIXINT = 0x7f; +const quint8 FIXMAP = 0x80; +const quint8 FIXARRAY = 0x90; +const quint8 FIXSTR = 0xa0; +const quint8 NIL = 0xc0; +const quint8 NEVER_USED = 0xc1; +const quint8 MFALSE = 0xc2; +const quint8 MTRUE = 0xc3; +const quint8 BIN8 = 0xc4; +const quint8 BIN16 = 0xc5; +const quint8 BIN32 = 0xc6; +const quint8 EXT8 = 0xc7; +const quint8 EXT16 = 0xc8; +const quint8 EXT32 = 0xc9; +const quint8 FLOAT32 = 0xca; +const quint8 FLOAT64 = 0xcb; +const quint8 UINT8 = 0xcc; +const quint8 UINT16 = 0xcd; +const quint8 UINT32 = 0xce; +const quint8 UINT64 = 0xcf; +const quint8 INT8 = 0xd0; +const quint8 INT16 = 0xd1; +const quint8 INT32 = 0xd2; +const quint8 INT64 = 0xd3; +const quint8 FIXEXT1 = 0xd4; +const quint8 FIXEXT2 = 0xd5; +const quint8 FIXEXT4 = 0xd6; +const quint8 FIXEXT8 = 0xd7; +const quint8 FIXEX16 = 0xd8; +const quint8 STR8 = 0xd9; +const quint8 STR16 = 0xda; +const quint8 STR32 = 0xdb; +const quint8 ARRAY16 = 0xdc; +const quint8 ARRAY32 = 0xdd; +const quint8 MAP16 = 0xde; +const quint8 MAP32 = 0xdf; +const quint8 NEGATIVE_FIXINT = 0xe0; +} } - #endif // COMMON_H diff --git a/src/msgpackstream.cpp b/src/msgpackstream.cpp new file mode 100644 index 0000000..cd71cff --- /dev/null +++ b/src/msgpackstream.cpp @@ -0,0 +1,551 @@ +#include "msgpackstream.h" +#include "private/pack_p.h" +#include +#include + +#undef CHECK_STREAM_PRECOND +#ifndef QT_NO_DEBUG +#define CHECK_STREAM_PRECOND(retVal) \ + if (!dev) { \ + qWarning("msgpack::Stream: No device"); \ + return retVal; \ + } +#else +#define CHECK_STREAM_PRECOND(retVal) \ + if (!dev) { \ + return retVal; \ + } +#endif + +#define CHECK_STREAM_WRITE_PRECOND(retVal) \ + CHECK_STREAM_PRECOND(retVal) \ + if (q_status != Ok) \ + return retVal; + +MsgPackStream::MsgPackStream() : + dev(0), owndev(false), q_status(Ok) +{ } + +MsgPackStream::MsgPackStream(QIODevice *d) : + dev(d), owndev(false), q_status(Ok) +{ } + +MsgPackStream::MsgPackStream(QByteArray *a, QIODevice::OpenMode mode) : + owndev(true), q_status(Ok) +{ + QBuffer *buf = new QBuffer(a); + buf->open(mode); + dev = buf; +} + +MsgPackStream::MsgPackStream(const QByteArray &a) : + owndev(true), q_status(Ok) +{ + QBuffer *buf = new QBuffer(); + buf->setData(a); + buf->open(QIODevice::ReadOnly); + dev = buf; +} + +MsgPackStream::~MsgPackStream() +{ + if (owndev) + delete dev; +} + +void MsgPackStream::setDevice(QIODevice *d) +{ + if (owndev) + delete dev; + dev = d; + owndev = false; +} + +bool MsgPackStream::atEnd() const +{ + return dev ? dev->atEnd() : true; +} + +MsgPackStream::Status MsgPackStream::status() const +{ + return q_status; +} + +void MsgPackStream::resetStatus() +{ + q_status = Ok; +} + +void MsgPackStream::setStatus(Status status) +{ + q_status = status; +} + +MsgPackStream &MsgPackStream::operator>>(bool &b) +{ + CHECK_STREAM_PRECOND(*this) + quint8 p[1]; + if (!dev->getChar((char *)p)) { + b = false; + setStatus(ReadPastEnd); + } else { + if (p[0] != MsgPack::FirstByte::MTRUE || + p[0] != MsgPack::FirstByte::MFALSE) + setStatus(ReadCorruptData); + b = (p[0] == MsgPack::FirstByte::MTRUE); + } + return *this; +} + +MsgPackStream &MsgPackStream::operator >>(quint8 &u8) +{ + CHECK_STREAM_PRECOND(*this); + quint64 u64; + if (unpack_ulonglong(u64) && u64 <= std::numeric_limits::max()) + u8 = u64; + else + setStatus(ReadCorruptData); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(quint16 &u16) +{ + CHECK_STREAM_PRECOND(*this); + quint64 u64; + if (unpack_ulonglong(u64) && u64 <= std::numeric_limits::max()) + u16 = u64; + else + setStatus(ReadCorruptData); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(quint32 &u32) +{ + CHECK_STREAM_PRECOND(*this); + quint64 u64; + if (unpack_ulonglong(u64) && u64 <= std::numeric_limits::max()) + u32 = u64; + else + setStatus(ReadCorruptData); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(quint64 &u64) +{ + CHECK_STREAM_PRECOND(*this); + unpack_ulonglong(u64); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(qint8 &i8) +{ + CHECK_STREAM_PRECOND(*this); + qint64 i64; + if (!unpack_longlong(i64)) + return *this; + if (i64 >= std::numeric_limits::min() && + i64 <= std::numeric_limits::max()) + i8 = i64; + else + setStatus(ReadCorruptData); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(qint16 &i16) +{ + CHECK_STREAM_PRECOND(*this); +qint64 i64; + if (!unpack_longlong(i64)) + return *this; + if (i64 >= std::numeric_limits::min() && + i64 <= std::numeric_limits::max()) + i16 = i64; + else + setStatus(ReadCorruptData); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(qint32 &i32) +{ + CHECK_STREAM_PRECOND(*this); + qint64 i64; + if (!unpack_longlong(i64)) + return *this; + if (i64 >= std::numeric_limits::min() && + i64 <= std::numeric_limits::max()) + i32 = i64; + else + setStatus(ReadCorruptData); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(qint64 &i64) +{ + CHECK_STREAM_PRECOND(*this); + unpack_longlong(i64); + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(float &f) +{ + CHECK_STREAM_PRECOND(*this); + quint8 *fp = (quint8 *)&f; + quint8 p[5]; + if (dev->read((char *)p, 1) != 1) { + setStatus(ReadPastEnd); + return *this; + } + if (p[0] != MsgPack::FirstByte::FLOAT32) { + setStatus(ReadCorruptData); + return *this; + } + if (dev->read((char *)p + 1, 4) != 4) { + setStatus(ReadPastEnd); + return *this; + } +#ifdef __LITTLE_ENDIAN__ + for (int i = 0; i < 4; ++i) + *(fp + 3 - i) = *(p + i + 1); +#else + for (int i = 0; i < 4; ++i) + *(fp + i) = *(p + i + 1); +#endif + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(double &d) +{ + CHECK_STREAM_PRECOND(*this); + quint8 *fp = (quint8 *)&d; + quint8 p[9]; + if (dev->read((char *)p, 1) != 1) { + setStatus(ReadPastEnd); + return *this; + } + if (p[0] != MsgPack::FirstByte::FLOAT64) { + setStatus(ReadCorruptData); + return *this; + } + if (dev->read((char *)p + 1, 8) != 8) { + setStatus(ReadPastEnd); + return *this; + } +#ifdef __LITTLE_ENDIAN__ + for (int i = 0; i < 8; ++i) + *(fp + 7 - i) = *(p + i + 1); +#else + for (int i = 0; i < 8; ++i) + *(fp + i) = *(p + i + 1); +#endif + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(QString &str) +{ + CHECK_STREAM_PRECOND(*this); + quint8 p[5]; + if (dev->read((char *)p, 1) != 1) { + setStatus(ReadPastEnd); + return *this; + } + + int len = 0; + if (*p >= 0xa0 && *p <= 0xbf) { // fixstr + len = (*p) & 0x1f; // 0b00011111 + } else if (*p == MsgPack::FirstByte::STR8) { + if (dev->read((char *)p + 1, 1) == 1) + len = p[1]; + } else if (*p == MsgPack::FirstByte::STR16) { + if (dev->read((char *)p + 1, 2) == 2) + len = _msgpack_load16(int, &p[1]); + } else if (*p == MsgPack::FirstByte::STR32) { + if (dev->read((char *)p + 1, 4) == 4) + len = _msgpack_load32(int, &p[1]); + } else { + setStatus(ReadCorruptData); + return *this; + } + quint8 *data = new quint8[len]; + if (dev->read((char *)data, len) != len) { + setStatus(ReadPastEnd); + delete[] data; + return *this; + } + str = QString::fromUtf8((const char*) data, len); + delete[] data; + return *this; +} + +MsgPackStream &MsgPackStream::operator>>(QByteArray &array) +{ + CHECK_STREAM_PRECOND(*this); + quint8 p[5]; + if (dev->read((char *)p, 1) != 1) { + setStatus(ReadPastEnd); + return *this; + } + quint32 len; + if (p[0] == MsgPack::FirstByte::BIN8) { + if (dev->read((char *)p + 1, 1) != 1) { + setStatus(ReadPastEnd); + return *this; + } + len = p[1]; + } else if (p[0] == MsgPack::FirstByte::BIN16) { + if (dev->read((char *)p + 1, 2) != 2) { + setStatus(ReadPastEnd); + return *this; + } + len = _msgpack_load16(quint16, &p[1]); + } else if (p[0] == MsgPack::FirstByte::BIN32) { + if (dev->read((char *)p + 1, 4) != 4) { + setStatus(ReadPastEnd); + return *this; + } + len = _msgpack_load32(quint32, &p[1]); + } else { + setStatus(ReadCorruptData); + return *this; + } + array.resize(len); + if (dev->read(array.data(), len) != len) + setStatus(ReadPastEnd); + return *this; +} + +bool MsgPackStream::readBytes(char *data, uint len) +{ + CHECK_STREAM_PRECOND(false); + return dev->read(data, len) == len; +} + +bool MsgPackStream::readNil() +{ + CHECK_STREAM_PRECOND(false); + quint8 b; + if (dev->read((char *)&b, 1) != 1 || + b != MsgPack::FirstByte::NIL) { + setStatus(ReadCorruptData); + return false; + } + return true; +} + +MsgPackStream &MsgPackStream::operator<<(bool b) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 m = b == true ? + MsgPack::FirstByte::MTRUE : MsgPack::FirstByte::MFALSE; + if (dev->write((char *)&m, 1) != 1) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(quint32 u32) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[5]; + quint8 sz = MsgPackPrivate::pack_uint(u32, p, true) - p; + if (dev->write((char *)p, sz) != sz) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(quint64 u64) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[9]; + quint8 sz = MsgPackPrivate::pack_ulonglong(u64, p, true) - p; + if (dev->write((char *)p, sz) != sz) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(qint32 i32) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[5]; + quint8 sz = MsgPackPrivate::pack_int(i32, p, true) - p; + if (dev->write((char *)p, sz) != sz) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(qint64 i64) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[9]; + quint8 sz = MsgPackPrivate::pack_longlong(i64, p, true) - p; + if (dev->write((char *)p, sz) != sz) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(float f) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[5]; + quint8 sz = MsgPackPrivate::pack_float(f, p, true) - p; + if (dev->write((char *)p, sz) != sz) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(double d) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[9]; + quint8 sz = MsgPackPrivate::pack_double(d, p, true) - p; + if (dev->write((char *)p, sz) != sz) + setStatus(WriteFailed); + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(QString str) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 *p = (quint8 *)0; + quint32 sz = MsgPackPrivate::pack_string(str, p, false) - p; + quint8 *data = new quint8[sz]; + MsgPackPrivate::pack_string(str, data, true); + if (dev->write((char *)data, sz) != sz) + setStatus(WriteFailed); + delete[] data; + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(const char *str) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 *p = (quint8 *)0; + quint32 str_len = strlen(str); + quint32 sz = MsgPackPrivate::pack_string_raw(str, str_len, p, false) - p; + quint8 *data = new quint8[sz]; + MsgPackPrivate::pack_string_raw(str, str_len, data, true); + if (dev->write((char *)data, sz) != sz) + setStatus(WriteFailed); + delete[] data; + return *this; +} + +MsgPackStream &MsgPackStream::operator<<(QByteArray array) +{ + CHECK_STREAM_WRITE_PRECOND(*this); + quint8 p[5]; + quint32 len = array.length(); + quint8 header_len = MsgPackPrivate::pack_bin_header(len, p, true) - p; + if (dev->write((char *)p, header_len) != header_len) { + setStatus(WriteFailed); + return *this; + } + if (dev->write(array.data(), len) != len) + setStatus(WriteFailed); + return *this; +} + +bool MsgPackStream::writeBytes(const char *data, uint len) +{ + CHECK_STREAM_WRITE_PRECOND(false); + if (dev->write(data, len) != len) { + setStatus(WriteFailed); + return false; + } + return true; +} + +bool MsgPackStream::writeNil() +{ + CHECK_STREAM_WRITE_PRECOND(false); + quint8 b = MsgPack::FirstByte::NIL; + if (dev->write((char *)&b, 1) != 1) { + setStatus(WriteFailed); + return false; + } + return true; +} + +bool MsgPackStream::unpack_longlong(qint64 &i64) +{ + quint8 p[9]; + if (dev->read((char *)p, 1) != 1) { + setStatus(ReadPastEnd); + return false; + } + + if (p[0] <= 127) {// positive fixint 0x00 - 0x7f + i64 = p[0]; + return true; + } else if (*p >= 0xe0) { // negative fixint 0xe0 - 0xff + i64 = (qint8)p[0]; + return true; + } + + static int typeLengths[] = {1, 2, 4, 8, 1, 2, 4, 8}; + int typeLength = typeLengths[p[0] - MsgPack::FirstByte::UINT8]; + if (dev->read((char *)p + 1, typeLength) != typeLength) { + setStatus(ReadPastEnd); + return false; + } + if (p[0] == MsgPack::FirstByte::UINT8) { + i64 = p[1]; + } else if (p[0] == MsgPack::FirstByte::INT8) { + i64 = (qint8)p[1]; + } else if (p[0] == MsgPack::FirstByte::UINT16) { + i64 = _msgpack_load16(quint16, p + 1); + } else if (p[0] == MsgPack::FirstByte::INT16) { + i64 = _msgpack_load16(qint16, p + 1); + } else if (p[0] == MsgPack::FirstByte::UINT32) { + i64 = _msgpack_load32(quint32, p + 1); + } else if (p[0] == MsgPack::FirstByte::INT32) { + i64 = _msgpack_load32(qint32, p + 1); + } else if (p[0] == MsgPack::FirstByte::UINT64) { + quint64 u64; + u64 = _msgpack_load64(quint64, p + 1); + if (u64 > std::numeric_limits::max()) { + setStatus(ReadCorruptData); + return false; + } + i64 = u64; + } else if (p[0] == MsgPack::FirstByte::INT64) { + i64 = _msgpack_load64(qint64, p + 1); + } + return true; +} + +bool MsgPackStream::unpack_ulonglong(quint64 &u64) +{ + quint8 p[9]; + if (dev->read((char *)p, 1) != 1) { + setStatus(ReadPastEnd); + return false; + } + + if (p[0] <= 127) {// positive fixint 0x00 - 0x7f + u64 = p[0]; + return true; + } else if (*p >= 0xe0) { // negative fixint 0xe0 - 0xff + return false; + } + + static int typeLengths[] = {1, 2, 4, 8, 1, 2, 4, 8}; + int typeLength = typeLengths[p[0] - MsgPack::FirstByte::UINT8]; + if (dev->read((char *)p + 1, typeLength) != typeLength) { + setStatus(ReadPastEnd); + return false; + } + if (p[0] == MsgPack::FirstByte::UINT8) { + u64 = p[1]; + return true; + } else if (p[0] == MsgPack::FirstByte::UINT16) { + u64 = _msgpack_load16(quint64, p + 1); + return true; + } else if (p[0] == MsgPack::FirstByte::UINT32) { + u64 = _msgpack_load32(quint64, p + 1); + return true; + } else if (p[0] == MsgPack::FirstByte::UINT64) { + u64 = _msgpack_load64(quint64, p + 1); + return true; + } + setStatus(ReadCorruptData); + return false; +} diff --git a/src/msgpackstream.h b/src/msgpackstream.h new file mode 100644 index 0000000..50ccb68 --- /dev/null +++ b/src/msgpackstream.h @@ -0,0 +1,245 @@ +#ifndef STREAM_H +#define STREAM_H +#include +#include +#include "msgpack_common.h" +#include "endianhelper.h" + +class MsgPackStream +{ +public: + MsgPackStream(); + MsgPackStream(QIODevice *d); + MsgPackStream(QByteArray *a, QIODevice::OpenMode mode); + MsgPackStream(const QByteArray &a); + virtual ~MsgPackStream(); + + void setDevice(QIODevice *d); + QIODevice *device() const; + bool atEnd() const; + + enum Status {Ok, ReadPastEnd, ReadCorruptData, WriteFailed}; + Status status() const; + void resetStatus(); + void setStatus(Status status); + + MsgPackStream &operator>>(bool &b); + MsgPackStream &operator>>(quint8 &u8); + MsgPackStream &operator>>(quint16 &u16); + MsgPackStream &operator>>(quint32 &u32); + MsgPackStream &operator>>(quint64 &u64); + MsgPackStream &operator>>(qint8 &i8); + MsgPackStream &operator>>(qint16 &i16); + MsgPackStream &operator>>(qint32 &i32); + MsgPackStream &operator>>(qint64 &i64); + MsgPackStream &operator>>(float &f); + MsgPackStream &operator>>(double &d); + MsgPackStream &operator>>(QString &str); + MsgPackStream &operator>>(QByteArray &array); + bool readBytes(char *data, uint len); + bool readNil(); + + MsgPackStream &operator<<(bool b); + MsgPackStream &operator<<(quint32 u32); + MsgPackStream &operator<<(quint64 u64); + MsgPackStream &operator<<(qint32 i32); + MsgPackStream &operator<<(qint64 i64); + MsgPackStream &operator<<(float f); + MsgPackStream &operator<<(double d); + MsgPackStream &operator<<(QString str); + MsgPackStream &operator<<(const char *str); + MsgPackStream &operator<<(QByteArray array); + bool writeBytes(const char *data, uint len); + bool writeNil(); + +private: + QIODevice *dev; + bool owndev; + Status q_status; + + bool unpack_longlong(qint64 &i64); + bool unpack_ulonglong(quint64 &u64); +}; + +template +MsgPackStream& operator<<(MsgPackStream& s, const QList &list) +{ + quint32 len = list.size(); + quint8 p[5]; + if (len <= 15) { + p[0] = MsgPack::FirstByte::FIXARRAY | len; + s.writeBytes((const char *)p, 1); + } else if (len <= std::numeric_limits::max()) { + p[0] = MsgPack::FirstByte::ARRAY16; + _msgpack_store16(p + 1, len); + s.writeBytes((const char *)p, 3); + } else { + p[0] = MsgPack::FirstByte::ARRAY32; + _msgpack_store32(p + 1, len); + s.writeBytes((const char *)p, 5); + } + + if (s.status() != MsgPackStream::Ok) + return s; + for (int i = 0; i < list.size(); ++i) + s << list[i]; + + return s; +} + +template +MsgPackStream& operator>>(MsgPackStream& s, QList &list) +{ + list.clear(); + + quint8 p[5]; + quint32 len; + s.readBytes((char *)p, 1); + if (p[0] >= 0x90 && p[0] <= 0x9f) { + len = p[0] & 0xf; + } else if (p[0] == MsgPack::FirstByte::ARRAY16) { + s.readBytes((char *)p + 1, 2); + len = _msgpack_load16(quint16, p + 1); + } else if (p[0] == MsgPack::FirstByte::ARRAY32) { + s.readBytes((char *)p + 1, 4); + len = _msgpack_load32(quint32, p + 1); + } + + for (quint32 i = 0; i < len; ++i) { + T t; + s >> t; + list.append(t); + if (s.atEnd()) + break; + } + + return s; +} + +template +MsgPackStream& operator<<(MsgPackStream &s, const QHash &hash) +{ + quint32 len = 0; + QHashIterator it(hash); + while (it.hasNext()) { + it.next(); + len++; + } + + quint8 p[5]; + if (len <= 15) { + p[0] = MsgPack::FirstByte::FIXMAP | len; + s.writeBytes((const char *)p, 1); + } else if (len <= std::numeric_limits::max()) { + p[0] = MsgPack::FirstByte::MAP16; + _msgpack_store16(p + 1, len); + s.writeBytes((const char *)p, 3); + } else { + p[0] = MsgPack::FirstByte::MAP32; + _msgpack_store32(p + 1, len); + s.writeBytes((const char *)p, 5); + } + + it.toFront(); + while (it.hasNext()) { + it.next(); + s << it.key() << it.value(); + } + + return s; +} + +template +MsgPackStream& operator>>(MsgPackStream& s, QHash &hash) +{ + hash.clear(); + + quint8 p[5]; + quint32 len; + s.readBytes((char *)p, 1); + if (p[0] >= 0x80 && p[0] <= 0x8f) { + len = p[0] & 0xf; + } else if (p[0] == MsgPack::FirstByte::MAP16) { + s.readBytes((char *)p + 1, 2); + len = _msgpack_load16(quint16, p + 1); + } else if (p[0] == MsgPack::FirstByte::MAP32) { + s.readBytes((char *)p + 1, 4); + len = _msgpack_load32(quint32, p + 1); + } + + for (quint32 i = 0; i < len; ++i) { + Key key; + T t; + s >> key >> t; + hash.insert(key, t); + if (s.atEnd()) + break; + } + + return s; +} + +template +MsgPackStream& operator<<(MsgPackStream &s, const QMap &map) +{ + quint32 len = 0; + QMapIterator it(map); + while (it.hasNext()) { + it.next(); + len++; + } + + quint8 p[5]; + if (len <= 15) { + p[0] = MsgPack::FirstByte::FIXMAP | len; + s.writeBytes((const char *)p, 1); + } else if (len <= std::numeric_limits::max()) { + p[0] = MsgPack::FirstByte::MAP16; + _msgpack_store16(p + 1, len); + s.writeBytes((const char *)p, 3); + } else { + p[0] = MsgPack::FirstByte::MAP32; + _msgpack_store32(p + 1, len); + s.writeBytes((const char *)p, 5); + } + + it.toFront(); + while (it.hasNext()) { + it.next(); + s << it.key() << it.value(); + } + + return s; +} + +template +MsgPackStream& operator>>(MsgPackStream& s, QMap &map) +{ + map.clear(); + + quint8 p[5]; + quint32 len; + s.readBytes((char *)p, 1); + if (p[0] >= 0x80 && p[0] <= 0x8f) { + len = p[0] & 0xf; + } else if (p[0] == MsgPack::FirstByte::MAP16) { + s.readBytes((char *)p + 1, 2); + len = _msgpack_load16(quint16, p + 1); + } else if (p[0] == MsgPack::FirstByte::MAP32) { + s.readBytes((char *)p + 1, 4); + len = _msgpack_load32(quint32, p + 1); + } + + for (quint32 i = 0; i < len; ++i) { + Key key; + T t; + s >> key >> t; + map.insert(key, t); + if (s.atEnd()) + break; + } + + return s; +} + +#endif // STREAM_H diff --git a/src/private/pack_p.cpp b/src/private/pack_p.cpp index 7e759d1..4a84c87 100644 --- a/src/private/pack_p.cpp +++ b/src/private/pack_p.cpp @@ -1,5 +1,5 @@ #include "pack_p.h" -#include "private/sysdep.h" +#include "../endianhelper.h" #include #include @@ -9,10 +9,14 @@ #include +#include +#include + QHash MsgPackPrivate::user_packers; bool MsgPackPrivate::compatibilityMode = false; +QReadWriteLock MsgPackPrivate::packers_lock; -quint8 *MsgPackPrivate::pack(const QVariant &v, quint8 *p, bool wr) +quint8 *MsgPackPrivate::pack(const QVariant &v, quint8 *p, bool wr, QVector &user_data) { QMetaType::Type t = (QMetaType::Type)v.type(); if (t == QMetaType::Int) @@ -24,7 +28,7 @@ quint8 *MsgPackPrivate::pack(const QVariant &v, quint8 *p, bool wr) else if (t == QMetaType::QString) p = pack_string(v.toString(), p, wr); else if (t == QMetaType::QVariantList) - p = pack_array(v.toList(), p, wr); + p = pack_array(v.toList(), p, wr, user_data); else if (t == QMetaType::QStringList) p = pack_stringlist(v.toStringList(), p, wr); else if (t == QMetaType::LongLong) @@ -33,16 +37,14 @@ quint8 *MsgPackPrivate::pack(const QVariant &v, quint8 *p, bool wr) p = pack_ulonglong(v.toULongLong(), p, wr); else if (t == QMetaType::Double) p = pack_double(v.toDouble(), p, wr); + else if (t == QMetaType::Float) + p = pack_float(v.toFloat(), p, wr); else if (t == QMetaType::QByteArray) p = pack_bin(v.toByteArray(), p, wr); else if (t == QMetaType::QVariantMap) - p = pack_map(v.toMap(), p, wr); - else { - if (user_packers.contains(t)) - p = pack_user(v, p, wr); - else - qWarning() << "MsgPack::pack can't pack type:" << t; - } + p = pack_map(v.toMap(), p, wr, user_data); + else + p = pack_user(v, p, wr, user_data); return p; } @@ -148,12 +150,12 @@ quint8 *MsgPackPrivate::pack_arraylen(quint32 len, quint8 *p, bool wr) return p; } -quint8 *MsgPackPrivate::pack_array(const QVariantList &list, quint8 *p, bool wr) +quint8 *MsgPackPrivate::pack_array(const QVariantList &list, quint8 *p, bool wr, QVector &user_data) { int len = list.length(); p = pack_arraylen(len, p, wr); foreach (QVariant item, list) - p = pack(item, p, wr); + p = pack(item, p, wr, user_data); return p; } @@ -166,9 +168,8 @@ quint8 *MsgPackPrivate::pack_stringlist(const QStringList &list, quint8 *p, bool return p; } -quint8 *MsgPackPrivate::pack_string(const QString &str, quint8 *p, bool wr) +quint8 *MsgPackPrivate::pack_string_raw(const char *str, quint32 len, quint8 *p, bool wr) { - int len = str.length(); if (len <= 31) { if (wr) *p = 0xa0 | len; p++; @@ -189,11 +190,35 @@ quint8 *MsgPackPrivate::pack_string(const QString &str, quint8 *p, bool wr) if (wr) _msgpack_store32(p, len); p += 4; } - if (wr) memcpy(p, str.toUtf8().data(), len); + if (wr) memcpy(p, str, len); return p + len; } +quint8 *MsgPackPrivate::pack_string(const QString &str, quint8 *p, bool wr) +{ + QByteArray str_data = str.toUtf8(); + quint32 str_len = str_data.length(); + return pack_string_raw(str_data.data(), str_len, p, wr); +} + +quint8 *MsgPackPrivate::pack_float(float f, quint8 *p, bool wr) +{ + if (wr) *p = 0xca; + p++; + if (wr) { + quint8 *d = (quint8 *)&f; +#ifdef __LITTLE_ENDIAN__ + for (int i = 0; i < 4; ++i) + *(p + 3 - i) = *(d + i); +#else + for (int i = 0; i < 4; ++i) + *(p + i) = *(d + i); +#endif + } + return p + 4; +} + quint8 *MsgPackPrivate::pack_double(double i, quint8 *p, bool wr) { if (wr) *p = 0xcb; @@ -211,9 +236,8 @@ quint8 *MsgPackPrivate::pack_double(double i, quint8 *p, bool wr) return p + 8; } -quint8 *MsgPackPrivate::pack_bin(const QByteArray &arr, quint8 *p, bool wr) +quint8 *MsgPackPrivate::pack_bin_header(quint32 len, quint8 *p, bool wr) { - int len = arr.length(); if (len <= std::numeric_limits::max()) { if (wr) *p = compatibilityMode ? 0xd9 : 0xc4; p++; @@ -230,14 +254,20 @@ quint8 *MsgPackPrivate::pack_bin(const QByteArray &arr, quint8 *p, bool wr) if (wr) _msgpack_store32(p, len); p += 4; } + return p; +} + +quint8 *MsgPackPrivate::pack_bin(const QByteArray &arr, quint8 *p, bool wr) +{ + quint32 len = arr.length(); + p = pack_bin_header(len, p, wr); if (wr) memcpy(p, arr.data(), len); p += len; - return p; } -quint8 *MsgPackPrivate::pack_map(const QVariantMap &map, quint8 *p, bool wr) +quint8 *MsgPackPrivate::pack_map(const QVariantMap &map, quint8 *p, bool wr, QVector &user_data) { QMapIterator it(map); int len = 0; @@ -263,23 +293,23 @@ quint8 *MsgPackPrivate::pack_map(const QVariantMap &map, quint8 *p, bool wr) it.toFront(); while (it.hasNext()) { it.next(); - p = pack(it.key(), p, wr); - p = pack(it.value(), p, wr); + p = pack(it.key(), p, wr, user_data); + p = pack(it.value(), p, wr, user_data); } return p; } - bool MsgPackPrivate::register_packer(QMetaType::Type q_type, qint8 msgpack_type, MsgPack::pack_user_f packer) { - if (user_packers.contains(q_type)) { - qWarning() << "MsgPack::packer for qtype" << q_type << "already exist"; - return false; - } if (packer == 0) { qWarning() << "MsgPack::packer for qtype" << q_type << "is invalid"; return false; } + QWriteLocker locker(&packers_lock); + if (user_packers.contains(q_type)) { + qWarning() << "MsgPack::packer for qtype" << q_type << "already exist"; + return false; + } packer_t p; p.packer = packer; p.type = msgpack_type; @@ -287,13 +317,34 @@ bool MsgPackPrivate::register_packer(QMetaType::Type q_type, qint8 msgpack_type, return true; } -quint8 *MsgPackPrivate::pack_user(const QVariant &v, quint8 *p, bool wr) +quint8 *MsgPackPrivate::pack_user(const QVariant &v, quint8 *p, bool wr, QVector &user_data) { - QMetaType::Type t = (QMetaType::Type)v.type() == QMetaType::User ? - (QMetaType::Type)v.userType() : (QMetaType::Type)v.type(); - QByteArray data; + QMetaType::Type t; + if (v.type() == QVariant::UserType) + t = (QMetaType::Type)v.userType(); + else + t = (QMetaType::Type)v.type(); + + QReadLocker locker(&packers_lock); + bool has_packer = user_packers.contains(t); + if (!has_packer) { + qWarning() << "MsgPack::pack can't pack type:" << t; + return p; + } packer_t pt = user_packers[t]; - quint32 len = pt.packer(v, data, wr); + locker.unlock(); + + QByteArray data; + if (wr) { + data = user_data.front(); + user_data.pop_front(); + + } else { + data = pt.packer(v); + user_data.push_back(data); + } + + quint32 len = data.size(); if (len == 1) { if (wr) *p = 0xd4; p++; @@ -331,4 +382,3 @@ quint8 *MsgPackPrivate::pack_user(const QVariant &v, quint8 *p, bool wr) memcpy(p, data.data(), len); return p += len; } - diff --git a/src/private/pack_p.h b/src/private/pack_p.h index b29f971..db29911 100644 --- a/src/private/pack_p.h +++ b/src/private/pack_p.h @@ -5,6 +5,7 @@ #include #include +#include class QByteArray; class QString; @@ -19,9 +20,10 @@ typedef struct { } packer_t; bool register_packer(QMetaType::Type q_type, qint8 msgpack_type, MsgPack::pack_user_f packer); extern QHash user_packers; +extern QReadWriteLock packers_lock; extern bool compatibilityMode; -quint8 * pack(const QVariant &v, quint8 *p, bool wr); +quint8 * pack(const QVariant &v, quint8 *p, bool wr, QVector &user_data); quint8 * pack_int(qint32 i, quint8 *p, bool wr); quint8 * pack_uint(quint32 i, quint8 *p, bool wr); @@ -31,14 +33,17 @@ quint8 * pack_ulonglong(quint64 i, quint8 *p, bool wr); quint8 * pack_bool(const QVariant &v, quint8 *p, bool wr); quint8 * pack_arraylen(quint32 len, quint8 *p, bool wr); -quint8 * pack_array(const QVariantList &list, quint8 *p, bool wr); +quint8 * pack_array(const QVariantList &list, quint8 *p, bool wr, QVector &user_data); quint8 * pack_stringlist(const QStringList &list, quint8 *p, bool wr); +quint8 * pack_string_raw(const char *str, quint32 len, quint8 *p, bool wr); quint8 * pack_string(const QString &str, quint8 *p, bool wr); +quint8 * pack_float(float f, quint8 *p, bool wr); quint8 * pack_double(double i, quint8 *p, bool wr); +quint8 * pack_bin_header(quint32 len, quint8 *p, bool wr); quint8 * pack_bin(const QByteArray &arr, quint8 *p, bool wr); -quint8 * pack_map(const QVariantMap &map, quint8 *p, bool wr); -quint8 * pack_user(const QVariant &v, quint8 *p, bool wr); +quint8 * pack_map(const QVariantMap &map, quint8 *p, bool wr, QVector &user_data); +quint8 * pack_user(const QVariant &v, quint8 *p, bool wr, QVector &user_data); } #endif // PACK_P_H diff --git a/src/private/qt_types_p.cpp b/src/private/qt_types_p.cpp new file mode 100644 index 0000000..56246b5 --- /dev/null +++ b/src/private/qt_types_p.cpp @@ -0,0 +1,251 @@ +#include "qt_types_p.h" +#include "pack_p.h" +#include "unpack_p.h" +#include "msgpackstream.h" +#include "endianhelper.h" +#include + +#ifdef QT_GUI_LIB +#include +#endif +#define NO_QTGUI_WARNING "Library built without QtGui, hence some types are not available" + +#include +#include + +bool MsgPackPrivate::register_qtype(QMetaType::Type q_type, quint8 msgpack_type) +{ + if (q_type == QMetaType::QColor) { + #ifdef QT_GUI_LIB + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qcolor); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qcolor); + #else + qWarning() << NO_QTGUI_WARNING; + return false; + #endif //QT_GUI_LIB + } else if (q_type == QMetaType::QTime) { + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qtime); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qtime); + } else if (q_type == QMetaType::QDate) { + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qdate); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qdate); + } else if (q_type == QMetaType::QDateTime) { + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qdatetime); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qdatetime); + } else if (q_type == QMetaType::QPoint) { + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qpoint); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qpoint); + } else if (q_type == QMetaType::QSize) { + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qsize); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qsize); + } else if (q_type == QMetaType::QRect) { + MsgPackPrivate::register_packer(q_type, msgpack_type, pack_qrect); + MsgPackPrivate::register_unpacker(msgpack_type, unpack_qrect); + } + return true; +} + +#ifdef QT_GUI_LIB +QByteArray MsgPackPrivate::pack_qcolor(const QVariant &variant) +{ + QByteArray data; + data.resize(4); + QColor color = variant.value(); + quint8 *p = (quint8 *)data.data(); + p[0] = color.red(); + p[1] = color.green(); + p[2] = color.blue(); + p[3] = color.alpha(); + return data; +} + +QVariant MsgPackPrivate::unpack_qcolor(const QByteArray &data) +{ + 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) +{ + quint8 hm = 0, ms = 0; + 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); +} + +QByteArray MsgPackPrivate::pack_qtime(const QVariant &variant) +{ + QTime time = variant.toTime(); + if (time.isNull()) + return QByteArray("\xc0", 1); + quint8 size = time.msec() == 0 ? 2 : 4; + QByteArray data; + data.resize(size); + 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.isNull()) + return QByteArray("\xc0", 1); + QByteArray data; + data.resize(3); + pack_qdate_raw(variant.toDate(), (quint8 *)data.data()); + return data; +} + +QVariant MsgPackPrivate::unpack_qdate(const QByteArray &data) +{ + if (data.size() == 1) + return QDate(); + return unpack_qdate_raw((quint8 *)data.data()); +} + +QByteArray MsgPackPrivate::pack_qdatetime(const QVariant &variant) +{ + QDateTime dt = variant.toDateTime(); + if (dt.isNull()) + return QByteArray("\xc0", 1); + quint8 time_size = dt.time().msec() == 0 ? 2 : 4; + QByteArray data; + data.resize(3 + time_size); + quint8 *p = (quint8 *)data.data(); + pack_qdate_raw(dt.date(), p); + p += 3; + pack_qtime_raw(dt.time(), p); + return data; +} + +QVariant MsgPackPrivate::unpack_qdatetime(const QByteArray &data) +{ + if (data.size() == 1) + return QDateTime(); + quint8 *p = (quint8 *)data.data(); + QDate d = unpack_qdate_raw(p); + QTime t = unpack_qtime_raw(p + 3, data.size() == 7); + return QDateTime(d, t); +} + +// Points and Vectors +QByteArray MsgPackPrivate::pack_qpoint(const QVariant &variant) +{ + QPoint point = variant.toPoint(); + if (point.isNull()) + return QByteArray("\xc0", 1); + QByteArray packed; + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << point.x() << point.y(); + return packed; +} + +QVariant MsgPackPrivate::unpack_qpoint(const QByteArray &data) +{ + if (data.size() == 1) + return QPoint(); + MsgPackStream stream(data); + qint32 x, y; + stream >> x >> y; + return QPoint(x, y); +} + +QByteArray MsgPackPrivate::pack_qsize(const QVariant &variant) +{ + QSize size = variant.toSize(); + if (size.isNull()) + return QByteArray("\xc0", 1); + QByteArray packed; + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << size.width() << size.height(); + return packed; +} + +QVariant MsgPackPrivate::unpack_qsize(const QByteArray &data) +{ + if (data.size() == 1) + return QSize(); + MsgPackStream stream(data); + qint32 width, height; + stream >> width >> height; + return QSize(width, height); +} + +QByteArray MsgPackPrivate::pack_qrect(const QVariant &variant) +{ + QRect rect = variant.toRect(); + if (rect.isNull()) + return QByteArray("\xc0", 1); + QPoint pt1 = rect.topLeft(); + QPoint pt2 = rect.bottomRight(); + QByteArray packed; + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << pt1.x() << pt1.y() << pt2.x() << pt2.y(); + return packed; +} + +QVariant MsgPackPrivate::unpack_qrect(const QByteArray &data) +{ + if (data.size() == 1) + return QRect(); + MsgPackStream stream(data); + qint32 x, y; + stream >> x >> y; + QRect rect; + rect.setTopLeft(QPoint(x, y)); + stream >> x >> y; + rect.setBottomRight(QPoint(x, y)); + return rect; +} + diff --git a/src/private/qt_types_p.h b/src/private/qt_types_p.h new file mode 100644 index 0000000..e0fbc9c --- /dev/null +++ b/src/private/qt_types_p.h @@ -0,0 +1,59 @@ +#ifndef QT_TYPES_P_H +#define QT_TYPES_P_H + +#include +#include + +namespace MsgPackPrivate +{ +bool register_qtype(QMetaType::Type q_type, quint8 msgpack_type); + +#ifdef QT_GUI_LIB +QByteArray pack_qcolor(const QVariant &variant); +QVariant unpack_qcolor(const QByteArray &data); +#endif //QT_GUI_LIB + +// Date and Time +/** + * @brief pack_qtime_raw internal: packs QTime to 4 or 2 bytes (with or without ms) + * @param time QTime to pack + * @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); + +/** + * @brief pack_qdate_raw internal: pack QDate to 3 bytes + * @param date QDate to pack + * @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); + +QByteArray pack_qdatetime(const QVariant &variant); +QVariant unpack_qdatetime(const QByteArray &data); + +// Points and Vectors +QByteArray pack_qpoint(const QVariant &variant); +QVariant unpack_qpoint(const QByteArray &data); +QByteArray pack_qsize(const QVariant &variant); +QVariant unpack_qsize(const QByteArray &data); +QByteArray pack_qrect(const QVariant &variant); +QVariant unpack_qrect(const QByteArray &data); + +} // MsgPackPrivate + +#endif // QT_TYPES_P_H diff --git a/src/private/unpack_p.cpp b/src/private/unpack_p.cpp index 511bd4a..cefdb99 100644 --- a/src/private/unpack_p.cpp +++ b/src/private/unpack_p.cpp @@ -1,10 +1,13 @@ #include "unpack_p.h" -#include "sysdep.h" +#include "../endianhelper.h" #include #include #include +#include +#include + MsgPackPrivate::type_parser_f MsgPackPrivate::unpackers[32] = { unpack_nil, unpack_never_used, @@ -21,6 +24,7 @@ MsgPackPrivate::type_parser_f MsgPackPrivate::unpackers[32] = { }; QHash MsgPackPrivate::user_unpackers; +QReadWriteLock MsgPackPrivate::unpackers_lock; QVariant MsgPackPrivate::unpack(quint8 *p, quint8 *end) { @@ -143,7 +147,7 @@ quint8 * MsgPackPrivate::unpack_int16(QVariant &v, quint8 *p) quint8 * MsgPackPrivate::unpack_int32(QVariant &v, quint8 *p) { p++; - v = _msgpack_load32(quint32, p); + v = _msgpack_load32(qint32, p); return p + 4; } @@ -179,7 +183,7 @@ quint8 * MsgPackPrivate::unpack_float64(QVariant &v, quint8 *p) for (int i = 0; i < 8; ++i) *(fd + 7 - i) = *(p + i); #else - for (int i = 0; i < 4; ++i) + for (int i = 0; i < 8; ++i) *(fp + i) = *(p + i); #endif v = d; @@ -314,6 +318,7 @@ quint8 * MsgPackPrivate::unpack_map32(QVariant &v, quint8 *p) quint8 *MsgPackPrivate::unpack_ext(QVariant &v, quint8 *p, qint8 type, quint32 len) { + QReadLocker locker(&unpackers_lock); if (!user_unpackers.contains(type)) { qWarning() << "MsgPack::unpack() unpacker for type" << type << "doesn't exist"; return p + len; @@ -382,14 +387,15 @@ quint8 * MsgPackPrivate::unpack_ext32(QVariant &v, quint8 *p) bool MsgPackPrivate::register_unpacker(qint8 msgpack_type, MsgPack::unpack_user_f unpacker) { - if (user_unpackers.contains(msgpack_type)) { - qWarning() << "MsgPack::unpacker for type" << msgpack_type << "already exists"; - return false; - } if (unpacker == 0) { qWarning() << "MsgPack::unpacker for type" << msgpack_type << "is invalid"; return false; } + QWriteLocker locker(&unpackers_lock); + if (user_unpackers.contains(msgpack_type)) { + qWarning() << "MsgPack::unpacker for type" << msgpack_type << "already exists"; + return false; + } user_unpackers.insert(msgpack_type, unpacker); return true; } diff --git a/src/private/unpack_p.h b/src/private/unpack_p.h index ed8b323..ae85648 100644 --- a/src/private/unpack_p.h +++ b/src/private/unpack_p.h @@ -5,6 +5,7 @@ #include #include +#include namespace MsgPackPrivate { @@ -19,6 +20,7 @@ extern type_parser_f unpackers[32]; bool register_unpacker(qint8 msgpack_type, MsgPack::unpack_user_f unpacker); extern QHash user_unpackers; +extern QReadWriteLock unpackers_lock; // goes from p to end unpacking types with unpack_type function below QVariant unpack(quint8 *p, quint8 *end); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 18f82e5..9e74114 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) +set(TEST_SUBDIRS pack unpack mixed qttypes stream) foreach(subdir ${TEST_SUBDIRS}) add_subdirectory(${subdir}) diff --git a/tests/mixed/mixed_test.cpp b/tests/mixed/mixed_test.cpp index 1976483..0f17b8d 100644 --- a/tests/mixed/mixed_test.cpp +++ b/tests/mixed/mixed_test.cpp @@ -8,11 +8,85 @@ class MixedTest : public QObject Q_OBJECT private Q_SLOTS: + void test_float(); + void test_double(); void test_map(); void test_ext(); void test_mixed(); }; +void MixedTest::test_float() +{ + float f; + QByteArray packed; + + packed = MsgPack::pack(0.0f); + f = MsgPack::unpack(packed).toFloat(); + QVERIFY(f == 0.0f); + QVERIFY(packed.size() == 5); + + packed = MsgPack::pack(-0.0f); + f = MsgPack::unpack(packed).toFloat(); + QVERIFY(f == -0.0f); + QVERIFY(packed.size() == 5); + + packed = MsgPack::pack(1.0f); + f = MsgPack::unpack(packed).toFloat(); + QVERIFY(f == 1.0f); + QVERIFY(packed.size() == 5); + + packed = MsgPack::pack(-1.0f); + f = MsgPack::unpack(packed).toFloat(); + QVERIFY(f == -1.0f); + QVERIFY(packed.size() == 5); + + packed = MsgPack::pack(32767.0f); + f = MsgPack::unpack(packed).toFloat(); + QVERIFY(f == 32767.0f); + QVERIFY(packed.size() == 5); + + packed = MsgPack::pack(-32767.0f); + f = MsgPack::unpack(packed).toFloat(); + QVERIFY(f == -32767.0f); + QVERIFY(packed.size() == 5); +} + +void MixedTest::test_double() +{ + double d; + QByteArray packed; + + packed = MsgPack::pack(0.0); + d = MsgPack::unpack(packed).toDouble(); + QVERIFY(d == 0.0); + QVERIFY(packed.size() == 9); + + packed = MsgPack::pack(-0.0); + d = MsgPack::unpack(packed).toDouble(); + QVERIFY(d == -0.0); + QVERIFY(packed.size() == 9); + + packed = MsgPack::pack(1.0); + d = MsgPack::unpack(packed).toDouble(); + QVERIFY(d == 1.0); + QVERIFY(packed.size() == 9); + + packed = MsgPack::pack(-1.0); + d = MsgPack::unpack(packed).toDouble(); + QVERIFY(d == -1.0); + QVERIFY(packed.size() == 9); + + packed = MsgPack::pack(32767.0); + d = MsgPack::unpack(packed).toDouble(); + QVERIFY(d == 32767.0); + QVERIFY(packed.size() == 9); + + packed = MsgPack::pack(-32767.0); + d = MsgPack::unpack(packed).toDouble(); + QVERIFY(d == -32767.0); + QVERIFY(packed.size() == 9); +} + void MixedTest::test_map() { QVariantMap map; @@ -66,17 +140,15 @@ private: Q_DECLARE_METATYPE(CustomType) -quint32 pack_custom_type(const QVariant &variant, QByteArray &data, bool write) +QByteArray pack_custom_type(const QVariant &variant) { CustomType ct = variant.value(); - if (write) { - data.resize(ct.size()); - quint8 *p = (quint8 *)data.data(); - for (int i = 0; i < ct.size(); ++i) - p[i] = 7; - } - - return ct.size(); + QByteArray data; + data.resize(ct.size()); + quint8 *p = (quint8 *)data.data(); + for (int i = 0; i < ct.size(); ++i) + p[i] = 7; + return data; } QVariant unpack_custom_type(const QByteArray &data) @@ -90,9 +162,11 @@ void MixedTest::test_ext() QVariant custom; custom.setValue(ct); - MsgPack::registerPacker((QMetaType::Type)qMetaTypeId(), + bool packer_registered = MsgPack::registerPacker((QMetaType::Type)qMetaTypeId(), 3, pack_custom_type); - MsgPack::registerUnpacker(3, unpack_custom_type); + QVERIFY(packer_registered); + bool unpacker_registered = MsgPack::registerUnpacker(3, unpack_custom_type); + QVERIFY(unpacker_registered); QByteArray arr = MsgPack::pack(custom); QVERIFY(arr.size() == 2 + ct.size()); diff --git a/tests/pack/pack_test.cpp b/tests/pack/pack_test.cpp index 70b2963..235dc63 100644 --- a/tests/pack/pack_test.cpp +++ b/tests/pack/pack_test.cpp @@ -216,14 +216,14 @@ void PackTest::test_float() void PackTest::test_str() { - QString str = QString::fromUtf8("msgpack rocks"); + QString str = QString("msgpack rocks"); QByteArray arr = MsgPack::pack(str); QVERIFY(arr.size() == 14); quint8 *p = (quint8 *)arr.data(); QVERIFY(p[0] == (0xa0 | str.length())); QVERIFY(memcmp(p + 1, str.toUtf8().data(), str.size()) == 0); - str = QString::fromUtf8(QByteArray(32, 'm')); + str = QString(QByteArray(32, 'm')); arr = MsgPack::pack(str); QVERIFY(arr.size() == 32 + 2); p = (quint8 *)arr.data(); @@ -231,7 +231,7 @@ void PackTest::test_str() QVERIFY(p[1] == 32); QVERIFY(memcmp(p + 2, str.toUtf8().data(), str.size()) == 0); - str = QString::fromUtf8(QByteArray(256, 's')); + str = QString(QByteArray(256, 's')); arr = MsgPack::pack(str); QVERIFY(arr.size() == 256 + 3); p = (quint8 *)arr.data(); @@ -240,7 +240,7 @@ void PackTest::test_str() QVERIFY(p[2] == 0x00); QVERIFY(memcmp(p + 3, str.toUtf8().data(), str.size()) == 0); - str = QString::fromUtf8(QByteArray(65536, 'g')); + str = QString(QByteArray(65536, 'g')); arr = MsgPack::pack(str); QVERIFY(arr.size() == 65536 + 5); p = (quint8 *)arr.data(); @@ -271,7 +271,7 @@ void PackTest::test_bin() QVERIFY(p[2] == 0x00); QVERIFY(memcmp(p + 3, ba.data(), ba.size()) == 0); - ba = QByteArray(65536, 'r'); + ba = QByteArray(65536, 'x'); arr = MsgPack::pack(ba); QVERIFY(arr.size() == ba.size() + 5); p = (quint8 *)arr.data(); diff --git a/tests/qttypes/CMakeLists.txt b/tests/qttypes/CMakeLists.txt new file mode 100644 index 0000000..9f37e5b --- /dev/null +++ b/tests/qttypes/CMakeLists.txt @@ -0,0 +1,24 @@ +set(QT_USE_QTTEST TRUE) + +if (NOT Qt5Core_FOUND) + include( ${QT_USE_FILE} ) +endif() + +include(AddFileDependencies) + +include_directories(../../src ${CMAKE_CURRENT_BINARY_DIR}) + +set(UNIT_TESTS qttypes_test) + +foreach(test ${UNIT_TESTS}) + message(status "Building ${test}") + add_executable(${test} ${test}.cpp) + + target_link_libraries(${test} + ${QT_LIBRARIES} + ${TEST_LIBRARIES} + qmsgpack + ) + + add_test(${test} ${test}) +endforeach() \ No newline at end of file diff --git a/tests/qttypes/qttypes_test.cpp b/tests/qttypes/qttypes_test.cpp new file mode 100644 index 0000000..2d3bff2 --- /dev/null +++ b/tests/qttypes/qttypes_test.cpp @@ -0,0 +1,144 @@ +#include +#include +#include +#include +#include + +class QtTypesTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void test_qtime(); + void test_qdate(); + void test_qpoint(); + void test_qsize(); + void test_qrect(); +}; + +void QtTypesTest::test_qtime() +{ + MsgPack::registerType(QMetaType::QTime, 0x77); + QTime t; + QByteArray packed = MsgPack::pack(t); + QTime t2 = MsgPack::unpack(packed).toTime(); + QVERIFY(t == t2); + QVERIFY(packed.size() == 3); // user, type, 0xc0 + + t = QTime(12, 01, 01, 0); + packed = MsgPack::pack(t); + t2 = MsgPack::unpack(packed).toTime(); + QVERIFY(t == t2); + QVERIFY(packed.size() == 4); + + t = QTime(12, 59, 59, 0); + packed = MsgPack::pack(t); + t2 = MsgPack::unpack(packed).toTime(); + QVERIFY(t == t2); + QVERIFY(packed.size() == 4); + + t = QTime(12, 34, 56, 999); + packed = MsgPack::pack(t); + t2 = MsgPack::unpack(packed).toTime(); + QVERIFY(t == t2); + QVERIFY(packed.size() == 6); +} + +void QtTypesTest::test_qdate() +{ + MsgPack::registerType(QMetaType::QDate, 0x78); + QDate d = QDate(); + QByteArray packed = MsgPack::pack(d); + QDate d2 = MsgPack::unpack(packed).toDate(); + QVERIFY(d == d2); + QVERIFY(packed.size() == 3); // user, type, 0xc0 + + d = QDate(1234, 12, 1); + packed = MsgPack::pack(d); + d2 = MsgPack::unpack(packed).toDate(); + QVERIFY(d == d2); + + d = QDate(9999, 1, 31); + packed = MsgPack::pack(d); + d2 = MsgPack::unpack(packed).toDate(); + QVERIFY(d == d2); +} + +void QtTypesTest::test_qpoint() +{ + MsgPack::registerType(QMetaType::QPoint, 0x79); + + QPoint pt; + QByteArray packed = MsgPack::pack(pt); + QVERIFY(packed.size() == 3); + QPoint pt2 = MsgPack::unpack(packed).toPoint(); + QVERIFY(pt == pt2); + + pt = QPoint(1, 2); + packed = MsgPack::pack(pt); + QVERIFY(packed.size() == 4); + pt2 = MsgPack::unpack(packed).toPoint(); + QVERIFY(pt == pt2); + + pt = QPoint(1234, 5678); + packed = MsgPack::pack(pt); + QVERIFY(packed.size() == 9); + pt2 = MsgPack::unpack(packed).toPoint(); + QVERIFY(pt == pt2); + + pt = QPoint(std::numeric_limits::max(), std::numeric_limits::max()); + packed = MsgPack::pack(pt); + QVERIFY(packed.size() == 13); + pt2 = MsgPack::unpack(packed).toPoint(); + QVERIFY(pt == pt2); +} + +void QtTypesTest::test_qsize() +{ + MsgPack::registerType(QMetaType::QSize, 80); + + QSize sz; + QByteArray packed = MsgPack::pack(sz); +// QVERIFY(packed.size() == 3); + QSize sz2 = MsgPack::unpack(packed).toSize(); + QVERIFY(sz == sz2); + + sz = QSize(1, 2); + packed = MsgPack::pack(sz); + QVERIFY(packed.size() == 4); + sz2 = MsgPack::unpack(packed).toSize(); + QVERIFY(sz == sz2); + + sz = QSize(1234, 5678); + packed = MsgPack::pack(sz); + QVERIFY(packed.size() == 9); + sz2 = MsgPack::unpack(packed).toSize(); + QVERIFY(sz == sz2); +} + +void QtTypesTest::test_qrect() +{ + MsgPack::registerType(QMetaType::QRect, 81); + QRect r; + QByteArray packed = MsgPack::pack(r); + QVERIFY(packed.size() == 3); + QRect r2 = MsgPack::unpack(packed).toRect(); + QVERIFY(r == r2); + + r = QRect(1, 2, 3, 4); + packed = MsgPack::pack(r); + QVERIFY(packed.size() == 6); + r2 = MsgPack::unpack(packed).toRect(); + QVERIFY(r == r2); + + qint32 max = std::numeric_limits::max(); + r = QRect(0, 0, max, max); + packed = MsgPack::pack(r); + QVERIFY(packed.size() == 15); + r2 = MsgPack::unpack(packed).toRect(); + QVERIFY(r == r2); +} + +QTEST_APPLESS_MAIN(QtTypesTest) + +#include "qttypes_test.moc" diff --git a/tests/stream/CMakeLists.txt b/tests/stream/CMakeLists.txt new file mode 100644 index 0000000..0ba81a3 --- /dev/null +++ b/tests/stream/CMakeLists.txt @@ -0,0 +1,24 @@ +set(QT_USE_QTTEST TRUE) + +if (NOT Qt5Core_FOUND) + include( ${QT_USE_FILE} ) +endif() + +include(AddFileDependencies) + +include_directories(../../src ${CMAKE_CURRENT_BINARY_DIR}) + +set(UNIT_TESTS stream_test) + +foreach(test ${UNIT_TESTS}) + message(status "Building ${test}") + add_executable(${test} ${test}.cpp) + + target_link_libraries(${test} + ${QT_LIBRARIES} + ${TEST_LIBRARIES} + qmsgpack + ) + + add_test(${test} ${test}) +endforeach() \ No newline at end of file diff --git a/tests/stream/stream_test.cpp b/tests/stream/stream_test.cpp new file mode 100644 index 0000000..61eac91 --- /dev/null +++ b/tests/stream/stream_test.cpp @@ -0,0 +1,413 @@ +#include +#include +#include +#include +#include + +class StreamTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void test_unpack_integers(); + void test_pack_integers(); + void test_unpack_string(); + void test_pack_string(); + void test_float(); + void test_double(); + void test_bin(); + void test_array(); + void test_map(); +}; + +void StreamTest::test_unpack_integers() +{ + QByteArray ints = QByteArray::fromBase64("AH//4cyAzP/Q39CAzQEAzf//0f9/0YAAz" + "gABAADO/////9L//3//0oAAAADPAAAAAQ" + "AAAADP///////////T/////3/////TgAA" + "AAAAAAAA="); + MsgPackStream stream(ints); + quint8 u8; + quint16 u16; + quint32 u32; + quint64 u64; + qint8 i8; + qint16 i16; + qint32 i32; + qint64 i64; + + stream >> u8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u8 == 0); + stream >> u8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u8 == 127); + stream >> i8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i8 == -1); + stream >> i8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i8 == -31); + stream >> u8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u8 == 128); + stream >> u8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u8 == 255); + stream >> i8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i8 == -33); + stream >> i8; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i8 == -128); + stream >> u16; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u16 == 256); + stream >> u32; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u32 == 65535); + stream >> i16; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i16 == -129); + stream >> i16; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i16 == -32768); + stream >> u32; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u32 == 65536); + stream >> u32; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u32 == 4294967295); + stream >> i32; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i32 == -32769); + stream >> i32; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i32 == -2147483648); + stream >> u64; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u64 == 4294967296); + stream >> u64; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(u64 == std::numeric_limits::max()); + stream >> i64; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i64 == -2147483649); + stream >> i64; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(i64 == std::numeric_limits::min()); +} + +void StreamTest::test_pack_integers() +{ + QByteArray packed; + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << 0 << 127 << -1 << -31 << 128 << 255 << -33 << -128 << 256; + QVERIFY(stream.status() == MsgPackStream::Ok); + stream << 65535 << -129 << -32768 << 65536; + QVERIFY(stream.status() == MsgPackStream::Ok); + stream << (quint32)4294967295 << -32769 << (qint32)-2147483648; + QVERIFY(stream.status() == MsgPackStream::Ok); + stream << (quint64)4294967296; + QVERIFY(stream.status() == MsgPackStream::Ok); + stream << std::numeric_limits::max(); + QVERIFY(stream.status() == MsgPackStream::Ok); + stream << (qint64)-2147483649; + QVERIFY(stream.status() == MsgPackStream::Ok); + stream << std::numeric_limits::min(); + QVERIFY(stream.status() == MsgPackStream::Ok); + + QVariantList l = MsgPack::unpack(packed).toList(); + QVERIFY(l[0].toInt() == 0); + QVERIFY(l[1].toInt() == 127); + QVERIFY(l[2].toInt() == -1); + QVERIFY(l[3].toInt() == -31); + QVERIFY(l[4].toInt() == 128); + QVERIFY(l[5].toInt() == 255); + QVERIFY(l[6].toInt() == -33); + QVERIFY(l[7].toInt() == -128); + QVERIFY(l[8].toInt() == 256); + QVERIFY(l[9].toInt() == 65535); + QVERIFY(l[10].toInt() == -129); + QVERIFY(l[11].toInt() == -32768); + QVERIFY(l[12].toInt() == 65536); + QVERIFY(l[13].toUInt() == 4294967295); + QVERIFY(l[14].toInt() == -32769); + QVERIFY(l[15].toInt() == -2147483648); + QVERIFY(l[16].toLongLong() == 4294967296); + QVERIFY(l[17].toULongLong() == std::numeric_limits::max()); + QVERIFY(l[18].toLongLong() == -2147483649); + QVERIFY(l[19].toLongLong() == std::numeric_limits::min()); +} + +void StreamTest::test_unpack_string() +{ + QString str = QString("msgpack rocks"); + QByteArray packed = MsgPack::pack(str); + QString str2; + + { + MsgPackStream stream(packed); + stream >> str2; + QVERIFY(str == str2); + } + { + str = QString(QByteArray(32, 'm')); + packed = MsgPack::pack(str); + MsgPackStream stream(packed); + stream >> str2; + QVERIFY(str == str2); + } + { + str = QString::fromUtf8(QByteArray(256, 's')); + packed = MsgPack::pack(str); + MsgPackStream stream(packed); + stream >> str2; + QVERIFY(str == str2); + } + { + str = QString(QByteArray(65536, 'g')); + packed = MsgPack::pack(str); + MsgPackStream stream(packed); + stream >> str2; + QVERIFY(str == str2); + } +} + +void StreamTest::test_pack_string() +{ + QByteArray packed; + MsgPackStream stream(&packed, QIODevice::WriteOnly); + QStringList strs; + stream << "abc"; + strs << "abc"; + QString s; + for (int i = 0; i < 8; ++i) + s += "xy"; + stream << s; + strs << s; + s = QString(); + for (int i = 0; i < 64; ++i) + s += "msgp"; + stream << s; + strs << s; + s = QString(); + for (int i = 0; i < 4096; ++i) + s += "messagepack test"; + stream << s; + strs << s; + stream << ""; + + QStringList l = MsgPack::unpack(packed).toStringList(); + QVERIFY(l[0] == strs[0]); + QVERIFY(l[1] == strs[1]); + QVERIFY(l[2] == strs[2]); + QVERIFY(l[3] == strs[3]); + QVERIFY(l[4].isEmpty()); +} + +void StreamTest::test_float() +{ + QByteArray packed; + { + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << -0.0f << 0.0f << -1.0f << 1.0f << -32767.0f << 32767.0f; + QVERIFY(packed.size() == 6 * 5); + QVERIFY(stream.status() == MsgPackStream::Ok); + } + + MsgPackStream stream(packed); + float f; + + stream >> f; + QVERIFY(f == -0.0f); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> f; + QVERIFY(f == 0.0f); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> f; + QVERIFY(f == -1.0f); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> f; + QVERIFY(f == 1.0f); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> f; + QVERIFY(f == -32767.0f); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> f; + QVERIFY(f == 32767.0f); + QVERIFY(stream.status() == MsgPackStream::Ok); +} + +void StreamTest::test_double() +{ + QByteArray packed; + { + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << -0.0 << 0.0 << -1.0 << 1.0 << -32767.0 << 32767.0; + QVERIFY(packed.size() == 6 * 9); + QVERIFY(stream.status() == MsgPackStream::Ok); + } + + MsgPackStream stream(packed); + double d; + + stream >> d; + QVERIFY(d == -0.0); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> d; + QVERIFY(d == 0.0); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> d; + QVERIFY(d == -1.0); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> d; + QVERIFY(d == 1.0); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> d; + QVERIFY(d == -32767.0); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> d; + QVERIFY(d == 32767.0); + QVERIFY(stream.status() == MsgPackStream::Ok); +} + +void StreamTest::test_bin() +{ + QByteArray ba1("msgpack"), ba2(256, 'r'), ba3(65536, 'x'); + QByteArray packed; + { + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << ba1 << ba2 << ba3; + } + QVERIFY(packed.size() == 7+2 + 256+3 + 65536+5); + + MsgPackStream stream(packed); + QByteArray ba; + + stream >> ba; + QVERIFY(ba == ba1); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> ba; + QVERIFY(ba == ba2); + QVERIFY(stream.status() == MsgPackStream::Ok); + + stream >> ba; + QVERIFY(ba == ba3); + QVERIFY(stream.status() == MsgPackStream::Ok); +} + +void StreamTest::test_array() +{ +{ + QList list; + list << 0 << 127 << -1 << -31 << 128 << 255 << -33 << -128 << 256; + list << 65535 << -129 << -32768 << 65536; + list << -32769 << (qint32)-2147483648; + list << (qint64)-2147483649; + list << std::numeric_limits::min(); + QByteArray packed; + { + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << list; + QVERIFY(stream.status() == MsgPackStream::Ok); + } + + QVariantList list2 = MsgPack::unpack(packed).toList(); + QVERIFY(list.size() == list2.size()); + for (int i = 0; i < list.size(); ++i) + QVERIFY(list[i] == list2[i]); + + packed = MsgPack::pack(list2); + MsgPackStream stream(packed); + QList list3; + stream >> list3; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(list2.size() == list3.size()); + for (int i = 0; i < list2.size(); ++i) + QVERIFY(list2[i] == list3[i]); +} +{ + QList list; + list << 6; + list << std::numeric_limits::min(); + list << std::numeric_limits::max(); + list << -4; + QByteArray packed; + { + MsgPackStream stream(&packed, QIODevice::WriteOnly); + stream << list; + QVERIFY(stream.status() == MsgPackStream::Ok); + } + + QVariantList list2 = MsgPack::unpack(packed).toList(); + QVERIFY(list.size() == list2.size()); + for (int i = 0; i < list.size(); ++i) + QVERIFY(list[i] == list2[i]); + + packed = MsgPack::pack(list2); + MsgPackStream stream(packed); + QList list3; + stream >> list3; + QVERIFY(stream.status() == MsgPackStream::Ok); + QVERIFY(list2.size() == list3.size()); + for (int i = 0; i < list2.size(); ++i) + QVERIFY(list2[i] == list3[i]); +} +} + +void StreamTest::test_map() +{ + QMap map, map2; + QByteArray ba; + + map.insert("m0", 0); + { + MsgPackStream stream(&ba, QIODevice::WriteOnly); + stream << map; + MsgPackStream stream2(ba); + stream2 >> map2; + } + QVERIFY(ba.length() == 5); + quint8 *p = (quint8 *)ba.data(); + QVERIFY(p[0] == 0x80 | 1); + QVERIFY(map == map2); + + for (int i = 1; i < 16; ++i) + map.insert(QString("m%1").QString::arg(i), i); + { + MsgPackStream stream(&ba, QIODevice::WriteOnly); + stream << map; + MsgPackStream stream2(ba); + stream2 >> map2; + } + p = (quint8 *)ba.data(); + QVERIFY(p[0] == 0xde); + QVERIFY(map == map2); + + for (int i = 16; i < 65536; ++i) + map.insert(QString("m%1").QString::arg(i), i); + { + MsgPackStream stream(&ba, QIODevice::WriteOnly); + stream << map; + MsgPackStream stream2(ba); + stream2 >> map2; + } + p = (quint8 *)ba.data(); + QVERIFY(p[0] == 0xdf); + QVERIFY(map == map2); +} + +QTEST_APPLESS_MAIN(StreamTest) +#include "stream_test.moc"