diff --git a/Files.cmake b/Files.cmake index 91b58ee2..e23a18d1 100644 --- a/Files.cmake +++ b/Files.cmake @@ -190,6 +190,7 @@ IF (MSGPACK_ENABLE_CXX) include/msgpack/adaptor/cpp11/forward_list.hpp include/msgpack/adaptor/cpp11/reference_wrapper.hpp include/msgpack/adaptor/cpp11/shared_ptr.hpp + include/msgpack/adaptor/cpp11/timespec.hpp include/msgpack/adaptor/cpp11/tuple.hpp include/msgpack/adaptor/cpp11/unique_ptr.hpp include/msgpack/adaptor/cpp11/unordered_map.hpp @@ -552,6 +553,7 @@ IF (MSGPACK_ENABLE_CXX) include/msgpack/v1/adaptor/cpp11/forward_list.hpp include/msgpack/v1/adaptor/cpp11/reference_wrapper.hpp include/msgpack/v1/adaptor/cpp11/shared_ptr.hpp + include/msgpack/v1/adaptor/cpp11/timespec.hpp include/msgpack/v1/adaptor/cpp11/tuple.hpp include/msgpack/v1/adaptor/cpp11/unique_ptr.hpp include/msgpack/v1/adaptor/cpp11/unordered_map.hpp diff --git a/include/msgpack/adaptor/cpp11/timespec.hpp b/include/msgpack/adaptor/cpp11/timespec.hpp new file mode 100644 index 00000000..23652ce6 --- /dev/null +++ b/include/msgpack/adaptor/cpp11/timespec.hpp @@ -0,0 +1,16 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2019 KONDO Takatoshi +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef MSGPACK_TYPE_CPP11_TIMESPEC_HPP +#define MSGPACK_TYPE_CPP11_TIMESPEC_HPP + +#include "msgpack/v1/adaptor/cpp11/timespec.hpp" + +#endif // MSGPACK_TYPE_CPP11_TIMESPEC_HPP diff --git a/include/msgpack/type.hpp b/include/msgpack/type.hpp index b98e7826..6a48c497 100644 --- a/include/msgpack/type.hpp +++ b/include/msgpack/type.hpp @@ -39,6 +39,7 @@ #include "adaptor/cpp11/forward_list.hpp" #include "adaptor/cpp11/reference_wrapper.hpp" #include "adaptor/cpp11/shared_ptr.hpp" +#include "adaptor/cpp11/timespec.hpp" #include "adaptor/cpp11/tuple.hpp" #include "adaptor/cpp11/unique_ptr.hpp" #include "adaptor/cpp11/unordered_map.hpp" diff --git a/include/msgpack/v1/adaptor/cpp11/timespec.hpp b/include/msgpack/v1/adaptor/cpp11/timespec.hpp new file mode 100644 index 00000000..dc047ee0 --- /dev/null +++ b/include/msgpack/v1/adaptor/cpp11/timespec.hpp @@ -0,0 +1,140 @@ +// +// MessagePack for C++ static resolution routine +// +// Copyright (C) 2018 KONDO Takatoshi +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef MSGPACK_V1_TYPE_CPP11_TIMESPEC_HPP +#define MSGPACK_V1_TYPE_CPP11_TIMESPEC_HPP + +#include "msgpack/versioning.hpp" +#include "msgpack/adaptor/adaptor_base.hpp" +#include "msgpack/object.hpp" + +#include + +namespace msgpack { + +/// @cond +MSGPACK_API_VERSION_NAMESPACE(v1) { +/// @endcond + +namespace adaptor { + +template <> +struct convert { + msgpack::object const& operator()(msgpack::object const& o, timespec& v) const { + if(o.type != msgpack::type::EXT) { throw msgpack::type_error(); } + if(o.via.ext.type() != -1) { throw msgpack::type_error(); } + switch(o.via.ext.size) { + case 4: { + uint32_t sec; + _msgpack_load32(uint32_t, o.via.ext.data(), &sec); + v.tv_sec = static_cast(sec); + v.tv_nsec = 0; + } break; + case 8: { + uint64_t value; + _msgpack_load64(uint64_t, o.via.ext.data(), &value); + v.tv_sec = static_cast(value & 0x00000003ffffffffLL); + v.tv_nsec= static_cast(value >> 34); + } break; + case 12: { + uint32_t nanosec; + _msgpack_load32(uint32_t, o.via.ext.data(), &nanosec); + int64_t sec; + _msgpack_load64(int64_t, o.via.ext.data() + 4, &sec); + v.tv_sec = static_cast(sec); + v.tv_nsec = static_cast(nanosec); + } break; + default: + throw msgpack::type_error(); + } + return o; + } +}; + +template <> +struct pack { + template + msgpack::packer& operator()(msgpack::packer& o, const timespec& v) const { + if ((static_cast(v.tv_sec) >> 34) == 0) { + uint64_t data64 = (static_cast(v.tv_nsec) << 34) | static_cast(v.tv_sec); + if ((data64 & 0xffffffff00000000L) == 0) { + // timestamp 32 + o.pack_ext(4, -1); + uint32_t data32 = static_cast(data64); + char buf[4]; + _msgpack_store32(buf, data32); + o.pack_ext_body(buf, 4); + } + else { + // timestamp 64 + o.pack_ext(8, -1); + char buf[8]; + _msgpack_store64(buf, data64); + o.pack_ext_body(buf, 8); + } + } + else { + // timestamp 96 + o.pack_ext(12, -1); + char buf[12]; + _msgpack_store32(&buf[0], static_cast(v.tv_nsec)); + _msgpack_store64(&buf[4], v.tv_sec); + o.pack_ext_body(buf, 12); + } + return o; + } +}; + +template <> +struct object_with_zone { + void operator()(msgpack::object::with_zone& o, const timespec& v) const { + if ((static_cast(v.tv_sec) >> 34) == 0) { + uint64_t data64 = (static_cast(v.tv_nsec) << 34) | static_cast(v.tv_sec); + if ((data64 & 0xffffffff00000000L) == 0) { + // timestamp 32 + o.type = msgpack::type::EXT; + o.via.ext.size = 4; + char* p = static_cast(o.zone.allocate_no_align(o.via.ext.size + 1)); + p[0] = static_cast(-1); + uint32_t data32 = static_cast(data64); + _msgpack_store32(&p[1], data32); + o.via.ext.ptr = p; + } + else { + // timestamp 64 + o.type = msgpack::type::EXT; + o.via.ext.size = 8; + char* p = static_cast(o.zone.allocate_no_align(o.via.ext.size + 1)); + p[0] = static_cast(-1); + _msgpack_store64(&p[1], data64); + o.via.ext.ptr = p; + } + } + else { + // timestamp 96 + o.type = msgpack::type::EXT; + o.via.ext.size = 12; + char* p = static_cast(o.zone.allocate_no_align(o.via.ext.size + 1)); + p[0] = static_cast(-1); + _msgpack_store32(&p[1], static_cast(v.tv_nsec)); + _msgpack_store64(&p[1 + 4], v.tv_sec); + o.via.ext.ptr = p; + } + } +}; + +} // namespace adaptor + +/// @cond +} // MSGPACK_API_VERSION_NAMESPACE(v1) +/// @endcond + +} // namespace msgpack + +#endif // MSGPACK_V1_TYPE_CPP11_TIMESPEC_HPP diff --git a/test/msgpack_cpp11.cpp b/test/msgpack_cpp11.cpp index c115c481..34588bf3 100644 --- a/test/msgpack_cpp11.cpp +++ b/test/msgpack_cpp11.cpp @@ -1049,4 +1049,158 @@ TEST(MSGPACK_CHRONO, system_clock_impl_now) EXPECT_EQ(val1, val3); } +TEST(MSGPACK_TIMESPEC, timespec_pack_convert_zero) +{ + std::stringstream ss; + timespec val1{ 0, 0 }; + + msgpack::pack(ss, val1); + EXPECT_EQ(ss.str().data()[0], static_cast(0xd6)); + + msgpack::object_handle oh; + msgpack::unpack(oh, ss.str().data(), ss.str().size()); + timespec val2 = oh.get().as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_object_with_zone_zero) +{ + msgpack::zone z; + timespec val1{ 0, 0 }; + msgpack::object obj(val1, z); + timespec val2 = obj.as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_pack_convert_32bit_sec) +{ + std::stringstream ss; + timespec val1{ 0xffffffffUL, 0 }; + + msgpack::pack(ss, val1); + EXPECT_EQ(ss.str().data()[0], static_cast(0xd6)); + + msgpack::object_handle oh; + msgpack::unpack(oh, ss.str().data(), ss.str().size()); + timespec val2 = oh.get().as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_object_with_zone_32bit_sec) +{ + msgpack::zone z; + timespec val1{ 0xffffffffUL, 0 }; + msgpack::object obj(val1, z); + timespec val2 = obj.as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_pack_convert_max_nano) +{ + std::stringstream ss; + timespec val1{ 0, 999999999 }; + + msgpack::pack(ss, val1); + EXPECT_EQ(ss.str().data()[0], static_cast(0xd7)); + msgpack::object_handle oh; + msgpack::unpack(oh, ss.str().data(), ss.str().size()); + timespec val2 = oh.get().as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_object_with_zone_max_nano) +{ + msgpack::zone z; + timespec val1{ 0, 999999999 }; + msgpack::object obj(val1, z); + timespec val2 = obj.as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_pack_convert_34bit_sec_max_nano) +{ + if (sizeof(decltype(std::declval().tv_sec)) <= 4) return; + std::stringstream ss; + timespec val1{ static_cast().tv_sec)>(0x3ffffffffULL), 999999999 }; + + msgpack::pack(ss, val1); + EXPECT_EQ(ss.str().data()[0], static_cast(0xd7)); + + msgpack::object_handle oh; + msgpack::unpack(oh, ss.str().data(), ss.str().size()); + timespec val2 = oh.get().as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_object_with_zone_34bit_sec_max_nano) +{ + if (sizeof(decltype(std::declval().tv_sec)) <= 4) return; + msgpack::zone z; + timespec val1{ static_cast().tv_sec)>(0x3ffffffffULL), 999999999 }; + msgpack::object obj(val1, z); + timespec val2 = obj.as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_pack_convert_35bit_sec_max_nano) +{ + if (sizeof(decltype(std::declval().tv_sec)) <= 4) return; + std::stringstream ss; + timespec val1{ static_cast().tv_sec)>(0x7ffffffffULL), 999999999 }; + + msgpack::pack(ss, val1); + EXPECT_EQ(ss.str().data()[0], static_cast(0xc7)); + + msgpack::object_handle oh; + msgpack::unpack(oh, ss.str().data(), ss.str().size()); + timespec val2 = oh.get().as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_object_with_zone_35bit_sec_max_nano) +{ + if (sizeof(decltype(std::declval().tv_sec)) <= 4) return; + msgpack::zone z; + timespec val1{ static_cast().tv_sec)>(0x7ffffffffULL), 999999999 }; + msgpack::object obj(val1, z); + timespec val2 = obj.as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_pack_convert_64bit_sec_max_nano) +{ + std::stringstream ss; + timespec val1{ std::numeric_limits().tv_sec)>::max(), 999999999 }; + + msgpack::pack(ss, val1); + EXPECT_EQ(ss.str().data()[0], static_cast(0xc7)); + + msgpack::object_handle oh; + msgpack::unpack(oh, ss.str().data(), ss.str().size()); + timespec val2 = oh.get().as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + +TEST(MSGPACK_TIMESPEC, timespec_object_with_zone_64bit_sec_max_nano) +{ + msgpack::zone z; + timespec val1{ std::numeric_limits().tv_sec)>::max(), 999999999 }; + msgpack::object obj(val1, z); + timespec val2 = obj.as(); + EXPECT_EQ(val1.tv_sec, val2.tv_sec); + EXPECT_EQ(val1.tv_nsec, val2.tv_nsec); +} + + #endif // !defined(MSGPACK_USE_CPP03)