Skip to content

Commit

Permalink
remove dependency on boost in chrono.hpp
Browse files Browse the repository at this point in the history
  • Loading branch information
uyha committed Jul 3, 2023
1 parent ac062e2 commit facbd0f
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 22 deletions.
122 changes: 104 additions & 18 deletions include/msgpack/v1/adaptor/cpp11/chrono.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,14 @@
#ifndef MSGPACK_V1_TYPE_CPP11_CHRONO_HPP
#define MSGPACK_V1_TYPE_CPP11_CHRONO_HPP

#if !defined(MSGPACK_NO_BOOST)

#include "msgpack/versioning.hpp"
#include "msgpack/adaptor/adaptor_base.hpp"
#include "msgpack/object.hpp"
#include "msgpack/adaptor/check_container_size.hpp"

#include <limits>
#include <chrono>

#include <boost/numeric/conversion/cast.hpp>

namespace msgpack {

/// @cond
Expand All @@ -30,6 +27,97 @@ MSGPACK_API_VERSION_NAMESPACE(v1) {

namespace adaptor {

namespace detail {
template<typename Target,
typename Source,
bool target_is_signed = std::is_signed<Target>::value,
bool source_is_signed = std::is_signed<Source>::value,
typename = typename std::enable_if<
std::is_integral<Target>::value &&
std::is_integral<Source>::value
>::type>
struct would_underflow {
// The default case includes the cases that Source being unsigned, and since Source
// is unsigned, no underflow can happen
would_underflow(Source) : value{false} {}
bool value;
};

template<typename Target, typename Source>
struct would_underflow<Target, Source, false, true> {
// When Source is signed and Target is unsigned, we only need to compare with 0 to
// detect underflow, this works correctly and also avoids warnign from the compiler
would_underflow(Source source) : value{source < 0} {}
bool value;
};
template<typename Target, typename Source>
struct would_underflow<Target, Source, true, true> {
// When Source and Target are signed, the promotion rules apply sensibly so we do not
// need to do anything
would_underflow(Source source) : value{source < std::numeric_limits<Target>::min()} {}
bool value;
};

template<typename Target,
typename Source,
bool target_is_signed = std::is_signed<Target>::value,
bool source_is_signed = std::is_signed<Source>::value,
typename = typename std::enable_if<
std::is_integral<Target>::value &&
std::is_integral<Source>::value
>::type>
struct would_overflow {
// The default case is Source and Target having the same signedness, the promotion
// rules also apply sensibly here so nothing special needs to be done
would_overflow(Source source) : value{source > std::numeric_limits<Target>::max()} {}
bool value;
};
template<typename Target, typename Source>
struct would_overflow <Target, Source, false, true> {
// When Target is unsigned and Source is signed, we cannot rely on the promotion
// rules.
would_overflow(Source source)
: value{ sizeof(Target) >= sizeof(Source)
// Given Source is signed, Target being unsigned and having at least the
// same size makes impossible to overflow
? false
// Source being larger than Target makes it safe to cast the maximum value
// of Target to Source
: source > static_cast<Source>(std::numeric_limits<Target>::max())} {}
bool value;
};
template<typename Target, typename Source>
struct would_overflow <Target, Source, true, false> {
// When Target is signed and Source is unsigned, we cannot rely on the promotion
// rules.
would_overflow(Source source)
: value{ sizeof(Target) > sizeof(Source)
// Target being larger than Source makes it impossible to overflow
? false
// Source being unsigned and having at least the size of Target makes it
// safe to cast the maximum value of Target to Source
: source > static_cast<Source>(std::numeric_limits<Target>::max())} {}
bool value;
};

template<typename Target,
typename Source,
typename = typename std::enable_if<
std::is_integral<Target>::value &&
std::is_integral<Source>::value
>::type>
Target integral_cast(Source source) {
if (would_underflow<Target, Source>(source).value) {
throw std::underflow_error{"casting from Source to Target causes an underflow error"};
}
if(would_overflow<Target, Source>(source).value) {
throw std::overflow_error{"casting from Source to Target causes an overflow error"};;
}

return static_cast<Target>(source);
}
}

template <typename Clock, typename Duration>
struct as<std::chrono::time_point<Clock, Duration>> {
typename std::chrono::time_point<Clock, Duration> operator()(msgpack::object const& o) const {
Expand All @@ -45,7 +133,7 @@ struct as<std::chrono::time_point<Clock, Duration>> {
case 8: {
uint64_t value;
_msgpack_load64(uint64_t, o.via.ext.data(), &value);
uint32_t nanosec = boost::numeric_cast<uint32_t>(value >> 34);
uint32_t nanosec = detail::integral_cast<uint32_t>(value >> 34);
uint64_t sec = value & 0x00000003ffffffffLL;
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(nanosec));
Expand All @@ -69,7 +157,7 @@ struct as<std::chrono::time_point<Clock, Duration>> {
else {
++sec;
tp += std::chrono::seconds(sec);
int64_t ns = boost::numeric_cast<int64_t>(nanosec) - 1000000000L;
int64_t ns = detail::integral_cast<int64_t>(nanosec) - 1000000000L;
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(ns));
}
Expand Down Expand Up @@ -98,7 +186,7 @@ struct convert<std::chrono::time_point<Clock, Duration>> {
case 8: {
uint64_t value;
_msgpack_load64(uint64_t, o.via.ext.data(), &value);
uint32_t nanosec = boost::numeric_cast<uint32_t>(value >> 34);
uint32_t nanosec = detail::integral_cast<uint32_t>(value >> 34);
uint64_t sec = value & 0x00000003ffffffffLL;
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(nanosec));
Expand All @@ -123,7 +211,7 @@ struct convert<std::chrono::time_point<Clock, Duration>> {
else {
++sec;
tp += std::chrono::seconds(sec);
int64_t ns = boost::numeric_cast<int64_t>(nanosec) - 1000000000L;
int64_t ns = detail::integral_cast<int64_t>(nanosec) - 1000000000L;
tp += std::chrono::duration_cast<Duration>(
std::chrono::nanoseconds(ns));
}
Expand All @@ -142,7 +230,7 @@ template <typename Clock, typename Duration>
struct pack<std::chrono::time_point<Clock, Duration>> {
template <typename Stream>
msgpack::packer<Stream>& operator()(msgpack::packer<Stream>& o, std::chrono::time_point<Clock, Duration> const& v) const {
int64_t count = boost::numeric_cast<int64_t>(v.time_since_epoch().count());
int64_t count = detail::integral_cast<int64_t>(v.time_since_epoch().count());
int64_t nano_num =
Duration::period::ratio::num *
(1000000000L / Duration::period::ratio::den);
Expand All @@ -158,11 +246,11 @@ struct pack<std::chrono::time_point<Clock, Duration>> {
/ Duration::period::ratio::den;

if ((sec >> 34) == 0) {
uint64_t data64 = (boost::numeric_cast<uint64_t>(nanosec) << 34) | boost::numeric_cast<uint64_t>(sec);
uint64_t data64 = (detail::integral_cast<uint64_t>(nanosec) << 34) | detail::integral_cast<uint64_t>(sec);
if ((data64 & 0xffffffff00000000L) == 0) {
// timestamp 32
o.pack_ext(4, -1);
uint32_t data32 = boost::numeric_cast<uint32_t>(data64);
uint32_t data32 = detail::integral_cast<uint32_t>(data64);
char buf[4];
_msgpack_store32(buf, data32);
o.pack_ext_body(buf, 4);
Expand All @@ -181,7 +269,7 @@ struct pack<std::chrono::time_point<Clock, Duration>> {
char buf[12];


_msgpack_store32(&buf[0], boost::numeric_cast<uint32_t>(nanosec));
_msgpack_store32(&buf[0], detail::integral_cast<uint32_t>(nanosec));
_msgpack_store64(&buf[4], sec);
o.pack_ext_body(buf, 12);
}
Expand All @@ -192,7 +280,7 @@ struct pack<std::chrono::time_point<Clock, Duration>> {
template <typename Clock, typename Duration>
struct object_with_zone<std::chrono::time_point<Clock, Duration>> {
void operator()(msgpack::object::with_zone& o, const std::chrono::time_point<Clock, Duration>& v) const {
int64_t count = boost::numeric_cast<int64_t>(v.time_since_epoch().count());
int64_t count = detail::integral_cast<int64_t>(v.time_since_epoch().count());

int64_t nano_num =
Duration::period::ratio::num *
Expand All @@ -208,14 +296,14 @@ struct object_with_zone<std::chrono::time_point<Clock, Duration>> {
* Duration::period::ratio::num
/ Duration::period::ratio::den;
if ((sec >> 34) == 0) {
uint64_t data64 = (boost::numeric_cast<uint64_t>(nanosec) << 34) | boost::numeric_cast<uint64_t>(sec);
uint64_t data64 = (detail::integral_cast<uint64_t>(nanosec) << 34) | detail::integral_cast<uint64_t>(sec);
if ((data64 & 0xffffffff00000000L) == 0) {
// timestamp 32
o.type = msgpack::type::EXT;
o.via.ext.size = 4;
char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1));
p[0] = static_cast<char>(-1);
uint32_t data32 = boost::numeric_cast<uint32_t>(data64);
uint32_t data32 = detail::integral_cast<uint32_t>(data64);
_msgpack_store32(&p[1], data32);
o.via.ext.ptr = p;
}
Expand All @@ -235,7 +323,7 @@ struct object_with_zone<std::chrono::time_point<Clock, Duration>> {
o.via.ext.size = 12;
char* p = static_cast<char*>(o.zone.allocate_no_align(o.via.ext.size + 1));
p[0] = static_cast<char>(-1);
_msgpack_store32(&p[1], boost::numeric_cast<uint32_t>(nanosec));
_msgpack_store32(&p[1], detail::integral_cast<uint32_t>(nanosec));
_msgpack_store64(&p[1 + 4], sec);
o.via.ext.ptr = p;
}
Expand All @@ -250,6 +338,4 @@ struct object_with_zone<std::chrono::time_point<Clock, Duration>> {

} // namespace msgpack

#endif // !defined(MSGPACK_NO_BOOST)

#endif // MSGPACK_V1_TYPE_CPP11_CHRONO_HPP
4 changes: 0 additions & 4 deletions test/msgpack_cpp11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,8 +870,6 @@ BOOST_AUTO_TEST_CASE(no_def_con_array_simple_buffer)
BOOST_CHECK(val1 == val2);
}

#if !defined(MSGPACK_NO_BOOST)

BOOST_AUTO_TEST_CASE(system_clock)
{
std::chrono::system_clock::time_point val1;
Expand Down Expand Up @@ -1437,8 +1435,6 @@ BOOST_AUTO_TEST_CASE(high_resolution_clock_impl_now)
BOOST_CHECK(val1 == val3);
}

#endif // !defined(MSGPACK_NO_BOOST)


BOOST_AUTO_TEST_CASE(timespec_pack_convert_zero)
{
Expand Down

0 comments on commit facbd0f

Please sign in to comment.