Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

remove dependency on boost in chrono.hpp #1076

Merged
merged 3 commits into from
Jul 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ Makefile
/test/streaming_c
/test/version
/test/zone

build
*-build
.cache
compile_commands.json
2 changes: 0 additions & 2 deletions include/msgpack/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,7 @@
#include "adaptor/cpp11/array_char.hpp"
#include "adaptor/cpp11/array_unsigned_char.hpp"

#if !defined(MSGPACK_NO_BOOST)
#include "adaptor/cpp11/chrono.hpp"
#endif // !defined(MSGPACK_NO_BOOST)

#include "adaptor/cpp11/forward_list.hpp"
#include "adaptor/cpp11/reference_wrapper.hpp"
Expand Down
138 changes: 120 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,113 @@ 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 warnings 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
// rule 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
// rule.
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
// rule.
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);
}
} // namespace detail

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 +149,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 +173,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 +202,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 +227,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 +246,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 +262,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 +285,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 +296,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 +312,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 +339,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 +354,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