diff --git a/.travis.yml b/.travis.yml index b37d3ee..0706c70 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,6 @@ before_install: script: - mkdir build - cd build - - cmake .. + - if [ -z ${QT4_BUILD+x} ]; then cmake -DBUILD_TESTS=TRUE -DQT4_BUILD=FALSE ..; else cmake -DBUILD_TESTS=TRUE -DQT4_BUILD=TRUE ..; fi - make - make test diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f09424..5791159 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,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 3d656d3..6c76121 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,5 @@ -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) +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}) @@ -31,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/msgpack_common.h b/src/msgpack_common.h index 9f399b2..68a6479 100644 --- a/src/msgpack_common.h +++ b/src/msgpack_common.h @@ -36,8 +36,8 @@ const quint8 FIXARRAY = 0x90; const quint8 FIXSTR = 0xa0; const quint8 NIL = 0xc0; const quint8 NEVER_USED = 0xc1; -const quint8 FALSE = 0xc2; -const quint8 TRUE = 0xc3; +const quint8 MFALSE = 0xc2; +const quint8 MTRUE = 0xc3; const quint8 BIN8 = 0xc4; const quint8 BIN16 = 0xc5; const quint8 BIN32 = 0xc6; diff --git a/src/msgpack_common.h.in b/src/msgpack_common.h.in index d39965a..aaf6d82 100644 --- a/src/msgpack_common.h.in +++ b/src/msgpack_common.h.in @@ -36,8 +36,8 @@ const quint8 FIXARRAY = 0x90; const quint8 FIXSTR = 0xa0; const quint8 NIL = 0xc0; const quint8 NEVER_USED = 0xc1; -const quint8 FALSE = 0xc2; -const quint8 TRUE = 0xc3; +const quint8 MFALSE = 0xc2; +const quint8 MTRUE = 0xc3; const quint8 BIN8 = 0xc4; const quint8 BIN16 = 0xc5; const quint8 BIN32 = 0xc6; diff --git a/src/stream.cpp b/src/msgpackstream.cpp similarity index 93% rename from src/stream.cpp rename to src/msgpackstream.cpp index 932498f..cd71cff 100644 --- a/src/stream.cpp +++ b/src/msgpackstream.cpp @@ -1,6 +1,6 @@ -#include "stream.h" -#include +#include "msgpackstream.h" #include "private/pack_p.h" +#include #include #undef CHECK_STREAM_PRECOND @@ -27,7 +27,7 @@ MsgPackStream::MsgPackStream() : { } MsgPackStream::MsgPackStream(QIODevice *d) : - dev(d), owndev(false) + dev(d), owndev(false), q_status(Ok) { } MsgPackStream::MsgPackStream(QByteArray *a, QIODevice::OpenMode mode) : @@ -89,10 +89,10 @@ MsgPackStream &MsgPackStream::operator>>(bool &b) b = false; setStatus(ReadPastEnd); } else { - if (p[0] != MsgPack::FirstByte::TRUE || - p[0] != MsgPack::FirstByte::FALSE) + if (p[0] != MsgPack::FirstByte::MTRUE || + p[0] != MsgPack::FirstByte::MFALSE) setStatus(ReadCorruptData); - b = (p[0] == MsgPack::FirstByte::TRUE); + b = (p[0] == MsgPack::FirstByte::MTRUE); } return *this; } @@ -273,6 +273,7 @@ MsgPackStream &MsgPackStream::operator>>(QString &str) } str = QString::fromUtf8((const char*) data, len); delete[] data; + return *this; } MsgPackStream &MsgPackStream::operator>>(QByteArray &array) @@ -318,11 +319,23 @@ bool MsgPackStream::readBytes(char *data, uint len) 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::TRUE : MsgPack::FirstByte::FALSE; + MsgPack::FirstByte::MTRUE : MsgPack::FirstByte::MFALSE; if (dev->write((char *)&m, 1) != 1) setStatus(WriteFailed); return *this; @@ -433,8 +446,21 @@ 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::writeNil() +{ + CHECK_STREAM_WRITE_PRECOND(false); + quint8 b = MsgPack::FirstByte::NIL; + if (dev->write((char *)&b, 1) != 1) { + setStatus(WriteFailed); + return false; + } return true; } diff --git a/src/stream.h b/src/msgpackstream.h similarity index 51% rename from src/stream.h rename to src/msgpackstream.h index 38a19b9..50ccb68 100644 --- a/src/stream.h +++ b/src/msgpackstream.h @@ -37,6 +37,7 @@ public: MsgPackStream &operator>>(QString &str); MsgPackStream &operator>>(QByteArray &array); bool readBytes(char *data, uint len); + bool readNil(); MsgPackStream &operator<<(bool b); MsgPackStream &operator<<(quint32 u32); @@ -49,6 +50,7 @@ public: MsgPackStream &operator<<(const char *str); MsgPackStream &operator<<(QByteArray array); bool writeBytes(const char *data, uint len); + bool writeNil(); private: QIODevice *dev; @@ -76,10 +78,12 @@ MsgPackStream& operator<<(MsgPackStream& s, const QList &list) _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; } @@ -87,6 +91,7 @@ template MsgPackStream& operator>>(MsgPackStream& s, QList &list) { list.clear(); + quint8 p[5]; quint32 len; s.readBytes((char *)p, 1); @@ -99,6 +104,7 @@ MsgPackStream& operator>>(MsgPackStream& s, QList &list) s.readBytes((char *)p + 1, 4); len = _msgpack_load32(quint32, p + 1); } + for (quint32 i = 0; i < len; ++i) { T t; s >> t; @@ -106,6 +112,133 @@ MsgPackStream& operator>>(MsgPackStream& s, QList &list) 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; } diff --git a/src/private/qt_types_p.cpp b/src/private/qt_types_p.cpp index 1f1a068..9ab73fe 100644 --- a/src/private/qt_types_p.cpp +++ b/src/private/qt_types_p.cpp @@ -1,9 +1,8 @@ #include "qt_types_p.h" #include "pack_p.h" #include "unpack_p.h" -#include "stream.h" -#include "../endianhelper.h" - +#include "msgpackstream.h" +#include "endianhelper.h" #include #ifdef QT_GUI_LIB @@ -182,7 +181,6 @@ QVariant MsgPackPrivate::unpack_qpoint(const QByteArray &data) MsgPackStream stream(data); qint32 x, y; stream >> x >> y; - qDebug() << "unpack qpoint stream" << (stream.status() == MsgPackStream::Ok); return QPoint(x, y); } diff --git a/tests/pack/pack_test.cpp b/tests/pack/pack_test.cpp index 2bd6aea..235dc63 100644 --- a/tests/pack/pack_test.cpp +++ b/tests/pack/pack_test.cpp @@ -216,7 +216,7 @@ void PackTest::test_float() void PackTest::test_str() { - QString str = QStringLiteral("msgpack rocks"); + QString str = QString("msgpack rocks"); QByteArray arr = MsgPack::pack(str); QVERIFY(arr.size() == 14); quint8 *p = (quint8 *)arr.data(); diff --git a/tests/stream/stream_test.cpp b/tests/stream/stream_test.cpp index eccf804..61eac91 100644 --- a/tests/stream/stream_test.cpp +++ b/tests/stream/stream_test.cpp @@ -1,7 +1,6 @@ #include #include -#include -#include +#include #include #include @@ -18,6 +17,7 @@ private Q_SLOTS: void test_double(); void test_bin(); void test_array(); + void test_map(); }; void StreamTest::test_unpack_integers() @@ -142,7 +142,7 @@ void StreamTest::test_pack_integers() void StreamTest::test_unpack_string() { - QString str = QStringLiteral("msgpack rocks"); + QString str = QString("msgpack rocks"); QByteArray packed = MsgPack::pack(str); QString str2; @@ -367,5 +367,47 @@ void StreamTest::test_array() } } +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"