diff --git a/rostime/include/ros/impl/duration.h b/rostime/include/ros/impl/duration.h index 371064f8..1b857d50 100644 --- a/rostime/include/ros/impl/duration.h +++ b/rostime/include/ros/impl/duration.h @@ -52,7 +52,10 @@ namespace ros { template T& DurationBase::fromSec(double d) { - sec = (int32_t)floor(d); + int64_t sec64 = (int64_t)floor(d); + if (sec64 < INT_MIN || sec64 > INT_MAX) + throw std::runtime_error("Duration is out of dual 32-bit range"); + sec = (int32_t)sec64; nsec = (int32_t)((d - (double)sec)*1000000000); return *static_cast(this); } @@ -60,18 +63,19 @@ namespace ros { template T& DurationBase::fromNSec(int64_t t) { - sec = (int32_t)(t / 1000000000); + int64_t sec64 = t / 1000000000; + if (sec64 < INT_MIN || sec64 > INT_MAX) + throw std::runtime_error("Duration is out of dual 32-bit range"); + sec = (int32_t)sec64; nsec = (int32_t)(t % 1000000000); - - normalizeSecNSecSigned(sec, nsec); - return *static_cast(this); } template T DurationBase::operator+(const T &rhs) const { - return T(sec + rhs.sec, nsec + rhs.nsec); + T t; + return t.fromNSec(toNSec() + rhs.toNSec()); } template @@ -83,13 +87,15 @@ namespace ros { template T DurationBase::operator-(const T &rhs) const { - return T(sec - rhs.sec, nsec - rhs.nsec); + T t; + return t.fromNSec(toNSec() - rhs.toNSec()); } template T DurationBase::operator-() const { - return T(-sec , -nsec); + T t; + return t.fromNSec(-toNSec()); } template diff --git a/rostime/include/ros/impl/time.h b/rostime/include/ros/impl/time.h index d07737dd..ec991e6b 100644 --- a/rostime/include/ros/impl/time.h +++ b/rostime/include/ros/impl/time.h @@ -76,8 +76,8 @@ namespace ros template D TimeBase::operator-(const T &rhs) const { - return D((int32_t)sec - (int32_t)rhs.sec, - (int32_t)nsec - (int32_t)rhs.nsec); // carry handled in ctor + D d; + return d.fromNSec(toNSec() - rhs.toNSec()); } template diff --git a/rostime/include/ros/time.h b/rostime/include/ros/time.h index 84d7e5f5..544f09e8 100644 --- a/rostime/include/ros/time.h +++ b/rostime/include/ros/time.h @@ -151,7 +151,10 @@ namespace ros double toSec() const { return (double)sec + 1e-9*(double)nsec; }; T& fromSec(double t) { - sec = (uint32_t)floor(t); + int64_t sec64 = (int64_t)floor(t); + if (sec64 < 0 || sec64 > UINT_MAX) + throw std::runtime_error("Duration is out of dual 32-bit range"); + sec = (int32_t)sec64; nsec = (uint32_t)boost::math::round((t-sec) * 1e9); // avoid rounding errors sec += (nsec / 1000000000ul); diff --git a/rostime/test/duration.cpp b/rostime/test/duration.cpp index cafc190a..ba286cda 100644 --- a/rostime/test/duration.cpp +++ b/rostime/test/duration.cpp @@ -48,6 +48,86 @@ TEST(Duration, sleepWithSimTime) ASSERT_FALSE(rc); } +TEST(Duration, castFromDoubleExceptions) +{ + ros::Time::init(); + + Duration d1, d2, d3, d4; + // Valid values to cast, must not throw exceptions + EXPECT_NO_THROW(d1.fromSec(-2147483648.0)); + EXPECT_NO_THROW(d2.fromSec(-2147483647.999999)); + EXPECT_NO_THROW(d3.fromSec(2147483647.0)); + EXPECT_NO_THROW(d4.fromSec(2147483647.999999)); + + // The next casts all incorrect. + EXPECT_THROW(d1.fromSec(2147483648.0), std::runtime_error); + EXPECT_THROW(d2.fromSec(6442450943.0), std::runtime_error); // It's 2^31 - 1 + 2^32, and it could pass the test. + EXPECT_THROW(d3.fromSec(-2147483648.001), std::runtime_error); + EXPECT_THROW(d4.fromSec(-6442450943.0), std::runtime_error); +} + +TEST(Duration, castFromInt64Exceptions) +{ + ros::Time::init(); + + Duration d1, d2, d3, d4; + // Valid values to cast, must not throw exceptions + EXPECT_NO_THROW(d1.fromNSec(-2147483648000000000)); + EXPECT_NO_THROW(d2.fromNSec(-2147483647999999999)); + EXPECT_NO_THROW(d3.fromNSec(2147483647000000000)); + EXPECT_NO_THROW(d4.fromNSec(2147483647999999999)); + + // The next casts all incorrect. + EXPECT_THROW(d1.fromSec(2147483648000000000), std::runtime_error); + EXPECT_THROW(d2.fromSec(4294967296000000000), std::runtime_error); + EXPECT_THROW(d3.fromSec(-2147483648000000001), std::runtime_error); + EXPECT_THROW(d4.fromSec(-6442450943000000000), std::runtime_error); +} + +TEST(Duration, arithmeticExceptions) +{ + ros::Time::init(); + + Duration d1(2147483647, 0); + Duration d2(2147483647, 999999999); + EXPECT_THROW(d1 + d2, std::runtime_error); + + Duration d3(-2147483648, 0); + Duration d4(2147483647, 0); + EXPECT_THROW(d3 - d4, std::runtime_error); + EXPECT_THROW(d4 - d3, std::runtime_error); + + Duration d5(-2147483647, 1); + Duration d6(-2, 999999999); + Duration d7; + EXPECT_NO_THROW(d7 = d5 + d6); + EXPECT_EQ(-2147483648000000000, d7.toNSec()); +} + +TEST(Duration, negativeSignExceptions) +{ + ros::Time::init(); + + Duration d1(2147483647, 0); + Duration d2(2147483647, 999999999); + Duration d3; + EXPECT_NO_THROW(d3 = -d1); + EXPECT_EQ(-2147483647000000000, d3.toNSec()); + EXPECT_NO_THROW(d3 = -d2); + EXPECT_EQ(-2147483647999999999, d3.toNSec()); + + Duration d4(-2147483647, 0); + Duration d5(-2147483648, 999999999); + Duration d6(-2147483648, 2); + Duration d7; + EXPECT_NO_THROW(d7 = -d4); + EXPECT_EQ(2147483647000000000, d7.toNSec()); + EXPECT_NO_THROW(d7 = -d5); + EXPECT_EQ(2147483647000000001, d7.toNSec()); + EXPECT_NO_THROW(d7 = -d6); + EXPECT_EQ(2147483647999999998, d7.toNSec()); +} + int main(int argc, char **argv){ testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/rostime/test/time.cpp b/rostime/test/time.cpp index bf36a54f..f9e19ec1 100644 --- a/rostime/test/time.cpp +++ b/rostime/test/time.cpp @@ -287,6 +287,76 @@ TEST(Time, ToFromBoost) } } +TEST(Time, CastFromDoubleExceptions) +{ + ros::Time::init(); + + Time t1, t2, t3; + // Valid values to cast, must not throw exceptions + EXPECT_NO_THROW(t1.fromSec(4294967295.0)); + EXPECT_NO_THROW(t2.fromSec(4294967295.999)); + EXPECT_NO_THROW(t3.fromSec(0.0000001)); + + // The next casts all incorrect. + EXPECT_THROW(t1.fromSec(4294967296.0), std::runtime_error); + EXPECT_THROW(t2.fromSec(-0.0001), std::runtime_error); + EXPECT_THROW(t3.fromSec(-4294967296.0), std::runtime_error); +} + +TEST(Time, OperatorMinusExceptions) +{ + ros::Time::init(); + + Time t1(2147483648, 0); + Time t2(2147483647, 999999999); + Time t3(2147483647, 999999998); + Time t4(4294967295, 999999999); + Time t5(4294967295, 999999998); + Time t6(0, 1); + + Duration d1(2147483647, 999999999); + Duration d2(-2147483648, 0); + Duration d3(-2147483648, 1); + Duration d4(0, 1); + + EXPECT_NO_THROW(t1 - t2); + EXPECT_NO_THROW(t3 - t2); + EXPECT_NO_THROW(t4 - t5); + + EXPECT_NO_THROW(t1 - d1); + EXPECT_NO_THROW(t5 - d1); + + EXPECT_THROW(t4 - t6, std::runtime_error); + EXPECT_THROW(t4 - t3, std::runtime_error); + + EXPECT_THROW(t1 - d2, std::runtime_error); + EXPECT_THROW(t2 - d2, std::runtime_error); + EXPECT_THROW(t4 - d3, std::runtime_error); +} + +TEST(Time, OperatorPlusExceptions) +{ + ros::Time::init(); + + Time t1(2147483648, 0); + Time t2(2147483647, 999999999); + Time t4(4294967295, 999999999); + Time t5(4294967295, 999999998); + + Duration d1(2147483647, 999999999); + Duration d2(-2147483648, 1); + Duration d3(0, 2); + Duration d4(0, 1); + + EXPECT_NO_THROW(t2 + d2); + EXPECT_NO_THROW(t1 + d1); + + EXPECT_THROW(t4 + d4, std::runtime_error); + EXPECT_THROW(t4 + d1, std::runtime_error); + EXPECT_THROW(t5 + d3, std::runtime_error); +} + + /************************************* Duration Tests *****************/ TEST(Duration, Comparitors)