fixed negative Times
diff --git a/aos/common/time.cc b/aos/common/time.cc
index c4f77a8..28e5cae 100644
--- a/aos/common/time.cc
+++ b/aos/common/time.cc
@@ -32,7 +32,7 @@
// That would let me create a MockTime clock source.
}
-void Time::EnableMockTime(const Time now) {
+void Time::EnableMockTime(const Time &now) {
mock_time_enabled = true;
MutexLocker time_mutex_locker(&time_mutex);
current_mock_time = now;
@@ -43,7 +43,7 @@
mock_time_enabled = false;
}
-void Time::SetMockTime(const Time now) {
+void Time::SetMockTime(const Time &now) {
MutexLocker time_mutex_locker(&time_mutex);
if (!mock_time_enabled) {
LOG(FATAL, "Tried to set mock time and mock time is not enabled\n");
@@ -51,6 +51,12 @@
current_mock_time = now;
}
+void Time::IncrementMockTime(const Time &amount) {
+ static ::aos::Mutex mutex;
+ ::aos::MutexLocker sync(&mutex);
+ SetMockTime(Now() + amount);
+}
+
Time Time::Now(clockid_t clock) {
if (mock_time_enabled) {
MutexLocker time_mutex_locker(&time_mutex);
@@ -58,9 +64,6 @@
} else {
timespec temp;
if (clock_gettime(clock, &temp) != 0) {
- // TODO(aschuh): There needs to be a pluggable low level logging interface
- // so we can break this dependency loop. This also would help during
- // startup.
LOG(FATAL, "clock_gettime(%jd, %p) failed with %d: %s\n",
static_cast<uintmax_t>(clock), &temp, errno, strerror(errno));
}
@@ -107,6 +110,10 @@
sec_ *= rhs; // better not overflow, or the result is just too big
nsec_ = temp % kNSecInSec;
sec_ += (temp - nsec_) / kNSecInSec;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
return *this;
}
const Time Time::operator*(int32_t rhs) const {
@@ -115,6 +122,10 @@
Time &Time::operator/=(int32_t rhs) {
nsec_ = (sec_ % rhs) * (kNSecInSec / rhs) + nsec_ / rhs;
sec_ /= rhs;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
return *this;
}
const Time Time::operator/(int32_t rhs) const {
@@ -128,6 +139,10 @@
const int wraps = nsec_ / kNSecInSec;
sec_ = wraps;
nsec_ -= kNSecInSec * wraps;
+ if (nsec_ < 0) {
+ nsec_ += kNSecInSec;
+ sec_ -= 1;
+ }
return *this;
}
const Time Time::operator%(int32_t rhs) const {
diff --git a/aos/common/time.h b/aos/common/time.h
index 6c80451..a35065d 100644
--- a/aos/common/time.h
+++ b/aos/common/time.h
@@ -23,16 +23,22 @@
// 0 <= nsec_ < kNSecInSec should always be true. All functions here will make
// sure that that is true if it was on all inputs (including *this).
//
+// Negative times are supported so that all of the normal arithmetic identities
+// work. nsec_ is still always positive.
+//
// The arithmetic and comparison operators are overloaded because they make
// complete sense and are very useful. The default copy and assignment stuff is
-// left because it works fine. Multiplication and division of Times by Times are
+// left because it works fine. Multiplication of Times by Times is
// not implemented because I can't think of any uses for them and there are
-// multiple ways to do it.
+// multiple ways to do it. Division of Times by Times is implemented as the
+// ratio of them. Multiplication, division, and modulus of Times by integers are
+// implemented as interpreting the argument as nanoseconds.
struct Time {
#ifdef SWIG
// All of the uses of constexpr here can safely be simply removed.
// NOTE: This means that relying on the fact that constexpr implicitly makes
-// member functions const is not safe.
+// member functions const is not valid, so they all have to be explicitly marked
+// const.
#define constexpr
#endif // SWIG
public:
@@ -83,7 +89,6 @@
static Time Now(clockid_t clock = kDefaultClock);
// Constructs a Time representing seconds.
- // TODO(brians): fix and test the negative cases for all of these
static constexpr Time InSeconds(double seconds) {
return (seconds < 0.0) ?
Time(static_cast<int32_t>(seconds) - 1,
@@ -94,8 +99,11 @@
// Constructs a time representing microseconds.
static constexpr Time InNS(int64_t nseconds) {
- return Time(nseconds / static_cast<int64_t>(kNSecInSec),
- nseconds % kNSecInSec);
+ return (nseconds < 0) ?
+ Time(nseconds / static_cast<int64_t>(kNSecInSec) - 1,
+ (nseconds % kNSecInSec) + kNSecInSec) :
+ Time(nseconds / static_cast<int64_t>(kNSecInSec),
+ nseconds % kNSecInSec);
}
// Constructs a time representing microseconds.
@@ -108,7 +116,10 @@
// Constructs a time representing mseconds.
static constexpr Time InMS(int mseconds) {
- return Time(mseconds / kMSecInSec, (mseconds % kMSecInSec) * kNSecInMSec);
+ return (mseconds < 0) ?
+ Time(mseconds / kMSecInSec - 1,
+ (mseconds % kMSecInSec) * kNSecInMSec + kNSecInSec) :
+ Time(mseconds / kMSecInSec, (mseconds % kMSecInSec) * kNSecInMSec);
}
// Checks whether or not this time is within amount nanoseconds of other.
@@ -138,7 +149,6 @@
}
// Returns the time represent in microseconds.
- // TODO(brians): test this
int64_t constexpr ToUSec() const {
return static_cast<int64_t>(sec_) * static_cast<int64_t>(kUSecInSec) +
(static_cast<int64_t>(nsec_) / static_cast<int64_t>(kNSecInUSec));
@@ -160,7 +170,6 @@
const Time operator-(const Time &rhs) const;
const Time operator*(int32_t rhs) const;
const Time operator/(int32_t rhs) const;
- // TODO(brians) test this
double operator/(const Time &rhs) const;
const Time operator%(int32_t rhs) const;
@@ -194,14 +203,12 @@
// Enables returning the mock time value for Now instead of checking the
// system clock. This should only be used when testing things depending on
// time, or many things may/will break.
- static void EnableMockTime(const Time now);
+ static void EnableMockTime(const Time &now);
// Sets now when time is being mocked.
- static void SetMockTime(const Time now);
- // Convenience function to just increment the mock time by a certain amount.
- static void IncrementMockTime(const Time amount) {
- // TODO(brians) make this thread safe so it's more useful?
- SetMockTime(Now() + amount);
- }
+ static void SetMockTime(const Time &now);
+ // Convenience function to just increment the mock time by a certain amount in
+ // a thread safe way.
+ static void IncrementMockTime(const Time &amount);
// Disables mocking time.
static void DisableMockTime();
@@ -212,7 +219,9 @@
static void CheckImpl(int32_t nsec);
void Check() { CheckImpl(nsec_); }
// A constexpr version of CheckImpl that returns the given value when it
- // succeeds.
+ // succeeds or evaluates to non-constexpr and returns 0 when it fails.
+ // This will result in the usual LOG(FATAL) if this is used where it isn't
+ // required to be constexpr or a compile error if it is.
static constexpr int32_t CheckConstexpr(int32_t nsec) {
return (nsec >= kNSecInSec || nsec < 0) ? CheckImpl(nsec), 0 : nsec;
}
diff --git a/aos/common/time_test.cc b/aos/common/time_test.cc
index e6b7bb0..3f53de3 100644
--- a/aos/common/time_test.cc
+++ b/aos/common/time_test.cc
@@ -56,6 +56,9 @@
EXPECT_EQ(MACRO_DARG(Time(57, 6500)), t + MACRO_DARG(Time(3, 6000)));
EXPECT_EQ(MACRO_DARG(Time(50, 300)),
t + MACRO_DARG(Time(-5, Time::kNSecInSec - 200)));
+ EXPECT_EQ(Time(-46, 500), t + Time(-100, 0));
+ EXPECT_EQ(Time(-47, Time::kNSecInSec - 500),
+ Time(-101, Time::kNSecInSec - 1000) + t);
}
TEST(TimeTest, Subtraction) {
Time t(54, 500);
@@ -66,28 +69,55 @@
t - MACRO_DARG(Time(0, Time::kNSecInSec - 100)));
EXPECT_EQ(MACRO_DARG(Time(55, 800)),
t - MACRO_DARG(Time(-2, Time::kNSecInSec - 300)));
+ EXPECT_EQ(Time(54, 5500), t - Time(-1, Time::kNSecInSec - 5000));
+ EXPECT_EQ(Time(-50, Time::kNSecInSec - 300),
+ Time(5, 200) - t);
}
TEST(TimeTest, Multiplication) {
Time t(54, Time::kNSecInSec / 3);
EXPECT_EQ(MACRO_DARG(Time(108, Time::kNSecInSec / 3 * 2)), t * 2);
EXPECT_EQ(MACRO_DARG(Time(271, Time::kNSecInSec / 3 * 2 - 1)), t * 5);
+ EXPECT_EQ(Time(-109, Time::kNSecInSec / 3 + 1), t * -2);
+ EXPECT_EQ(Time(-55, Time::kNSecInSec / 3 * 2 + 1), t * -1);
+ EXPECT_EQ(Time(-218, Time::kNSecInSec / 3 * 2 + 2), (t * -1) * 4);
}
-TEST(TimeTest, Division) {
- EXPECT_EQ(MACRO_DARG(Time(5, Time::kNSecInSec / 10 * 4 + 50)),
- MACRO_DARG(Time(54, 500)) / 10);
+TEST(TimeTest, DivisionByInt) {
+ EXPECT_EQ(Time(5, Time::kNSecInSec / 10 * 4 + 50), Time(54, 500) / 10);
+ EXPECT_EQ(Time(2, Time::kNSecInSec / 4 * 3),
+ Time(5, Time::kNSecInSec / 2) / 2);
+ EXPECT_EQ(Time(-3, Time::kNSecInSec / 4 * 3),
+ Time(-5, Time::kNSecInSec / 2) / 2);
+}
+TEST(TimeTest, DivisionByTime) {
+ EXPECT_DOUBLE_EQ(2, Time(10, 0) / Time(5, 0));
+ EXPECT_DOUBLE_EQ(9, Time(27, 0) / Time(3, 0));
+ EXPECT_DOUBLE_EQ(9.25, Time(37, 0) / Time(4, 0));
+ EXPECT_DOUBLE_EQ(5.25, Time(36, Time::kNSecInSec / 4 * 3) / Time(7, 0));
+ EXPECT_DOUBLE_EQ(-5.25, Time(-37, Time::kNSecInSec / 4) / Time(7, 0));
+ EXPECT_DOUBLE_EQ(-5.25, Time(36, Time::kNSecInSec / 4 * 3) / Time(-7, 0));
}
TEST(TimeTest, Comparisons) {
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) > Time(971, 253)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) >= Time(971, 253)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) < Time(971, 255)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) <= Time(971, 255)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) >= Time(971, 253)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) <= Time(971, 254)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) >= Time(971, 254)));
- EXPECT_TRUE(MACRO_DARG(Time(972, 254) > Time(971, 254)));
- EXPECT_TRUE(MACRO_DARG(Time(971, 254) < Time(972, 254)));
+ EXPECT_TRUE(Time(971, 254) > Time(971, 253));
+ EXPECT_TRUE(Time(971, 254) >= Time(971, 253));
+ EXPECT_TRUE(Time(971, 254) < Time(971, 255));
+ EXPECT_TRUE(Time(971, 254) <= Time(971, 255));
+ EXPECT_TRUE(Time(971, 254) >= Time(971, 253));
+ EXPECT_TRUE(Time(971, 254) <= Time(971, 254));
+ EXPECT_TRUE(Time(971, 254) >= Time(971, 254));
+ EXPECT_TRUE(Time(972, 254) > Time(971, 254));
+ EXPECT_TRUE(Time(971, 254) < Time(972, 254));
+
+ EXPECT_TRUE(Time(-971, 254) > Time(-971, 253));
+ EXPECT_TRUE(Time(-971, 254) >= Time(-971, 253));
+ EXPECT_TRUE(Time(-971, 254) < Time(-971, 255));
+ EXPECT_TRUE(Time(-971, 254) <= Time(-971, 255));
+ EXPECT_TRUE(Time(-971, 254) >= Time(-971, 253));
+ EXPECT_TRUE(Time(-971, 254) <= Time(-971, 254));
+ EXPECT_TRUE(Time(-971, 254) >= Time(-971, 254));
+ EXPECT_TRUE(Time(-972, 254) < Time(-971, 254));
+ EXPECT_TRUE(Time(-971, 254) > Time(-972, 254));
}
TEST(TimeTest, Within) {
@@ -95,54 +125,73 @@
EXPECT_FALSE(MACRO_DARG(Time(55, 5000).IsWithin(Time(55, 4900), 99)));
EXPECT_TRUE(MACRO_DARG(Time(5, 0).IsWithin(Time(4, Time::kNSecInSec - 200),
250)));
+ EXPECT_TRUE(Time(-5, Time::kNSecInSec - 200).IsWithin(Time(-4, 0), 250));
+ EXPECT_TRUE(Time(-5, 200).IsWithin(Time(-5, 0), 250));
}
-TEST(TimeTest, Modulo) {
+TEST(TimeTest, Modulus) {
EXPECT_EQ(MACRO_DARG(Time(0, Time::kNSecInSec / 10 * 2)),
MACRO_DARG(Time(50, 0) % (Time::kNSecInSec / 10 * 3)));
+ EXPECT_EQ(Time(-1, Time::kNSecInSec / 10 * 8),
+ Time(-50, 0) % (Time::kNSecInSec / 10 * 3));
+ EXPECT_EQ(Time(-1, Time::kNSecInSec / 10 * 8),
+ Time(-50, 0) % (-Time::kNSecInSec / 10 * 3));
+ EXPECT_EQ(Time(0, Time::kNSecInSec / 10 * 2),
+ Time(50, 0) % (-Time::kNSecInSec / 10 * 3));
}
+// TODO(brians): Finish tests for negatives from here on.
TEST(TimeTest, InSeconds) {
EXPECT_EQ(MACRO_DARG(Time(2, Time::kNSecInSec / 100 * 55 - 1)),
Time::InSeconds(2.55));
+ EXPECT_EQ(MACRO_DARG(Time(-3, Time::kNSecInSec / 100 * 45)),
+ Time::InSeconds(-2.55));
}
TEST(TimeTest, ToSeconds) {
- EXPECT_EQ(13.23, Time::InSeconds(13.23).ToSeconds());
+ EXPECT_DOUBLE_EQ(13.23, Time::InSeconds(13.23).ToSeconds());
+ EXPECT_NEAR(-13.23, Time::InSeconds(-13.23).ToSeconds(),
+ 1.0 / Time::kNSecInSec * 2);
}
-#ifdef __VXWORKS__
-TEST(TimeTest, ToTicks) {
- EXPECT_EQ(sysClkRateGet() / 100,
- MACRO_DARG(Time(0, Time::kNSecInSec / 100).ToTicks()));
-}
-TEST(TimeTest, InTicks) {
- EXPECT_EQ(MACRO_DARG(Time(2, Time::kNSecInSec)),
- Time::InTicks(sysClkRateGet() * 2.5));
-}
-#endif
-
TEST(TimeTest, InMS) {
Time t = Time::InMS(254971);
EXPECT_EQ(254, t.sec());
EXPECT_EQ(971000000, t.nsec());
+
+ Time t2 = Time::InMS(-254971);
+ EXPECT_EQ(-255, t2.sec());
+ EXPECT_EQ(Time::kNSecInSec - 971000000, t2.nsec());
+}
+
+TEST(TimeTest, ToMSec) {
+ EXPECT_EQ(254971, Time(254, 971000000).ToMSec());
+ EXPECT_EQ(-254971, Time(-255, Time::kNSecInSec - 971000000).ToMSec());
}
TEST(TimeTest, InNS) {
Time t = Time::InNS(static_cast<int64_t>(973254111971ll));
EXPECT_EQ(973, t.sec());
EXPECT_EQ(254111971, t.nsec());
+
+ Time t2 = Time::InNS(static_cast<int64_t>(-973254111971ll));
+ EXPECT_EQ(-974, t2.sec());
+ EXPECT_EQ(Time::kNSecInSec - 254111971, t2.nsec());
}
TEST(TimeTest, InUS) {
Time t = Time::InUS(254111971);
EXPECT_EQ(254, t.sec());
EXPECT_EQ(111971000, t.nsec());
+
+ Time t2 = Time::InUS(-254111971);
+ EXPECT_EQ(-255, t2.sec());
+ EXPECT_EQ(Time::kNSecInSec - 111971000, t2.nsec());
}
-TEST(TimeTest, ToMSec) {
- Time t(254, 971000000);
- EXPECT_EQ(254971, t.ToMSec());
+TEST(TimeTest, ToUSec) {
+ EXPECT_EQ(254000971, Time(254, 971000).ToUSec());
+ EXPECT_EQ(-254000971, Time(-255, Time::kNSecInSec - 971000).ToUSec());
}
TEST(TimeTest, Abs) {