Commit 24f37c34 authored by Jordan Taylor's avatar Jordan Taylor Committed by Commit Bot

Added min conversions for 'In' and 'From' conversions

Added corresponding unit tests for new conversions.

This change is needed in order to support new changes
to ScrollTimeline, which needs to be able to support
-infinity and infinity values. The infinity values
were already covered, but -infinity values were not.

https://chromium-review.googlesource.com/c/chromium/src/+/2028173

Change-Id: I634860353782cf56a560228bf37903395b400dc6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2038130Reviewed-by: default avatarClemens Arbesser <arbesser@google.com>
Reviewed-by: default avatarYuri Wiitala <miu@chromium.org>
Commit-Queue: Jordan Taylor <jortaylo@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#742689}
parent 064dd7c0
...@@ -42,6 +42,10 @@ int TimeDelta::InDays() const { ...@@ -42,6 +42,10 @@ int TimeDelta::InDays() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int>::max(); return std::numeric_limits<int>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
return static_cast<int>(delta_ / Time::kMicrosecondsPerDay); return static_cast<int>(delta_ / Time::kMicrosecondsPerDay);
} }
...@@ -50,6 +54,10 @@ int TimeDelta::InDaysFloored() const { ...@@ -50,6 +54,10 @@ int TimeDelta::InDaysFloored() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int>::max(); return std::numeric_limits<int>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
int result = delta_ / Time::kMicrosecondsPerDay; int result = delta_ / Time::kMicrosecondsPerDay;
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay); int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay);
if (remainder < 0) { if (remainder < 0) {
...@@ -63,6 +71,10 @@ int TimeDelta::InHours() const { ...@@ -63,6 +71,10 @@ int TimeDelta::InHours() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int>::max(); return std::numeric_limits<int>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
return static_cast<int>(delta_ / Time::kMicrosecondsPerHour); return static_cast<int>(delta_ / Time::kMicrosecondsPerHour);
} }
...@@ -71,6 +83,10 @@ int TimeDelta::InMinutes() const { ...@@ -71,6 +83,10 @@ int TimeDelta::InMinutes() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int>::max(); return std::numeric_limits<int>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int>::min();
}
return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute); return static_cast<int>(delta_ / Time::kMicrosecondsPerMinute);
} }
...@@ -79,6 +95,10 @@ double TimeDelta::InSecondsF() const { ...@@ -79,6 +95,10 @@ double TimeDelta::InSecondsF() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond; return static_cast<double>(delta_) / Time::kMicrosecondsPerSecond;
} }
...@@ -87,6 +107,10 @@ int64_t TimeDelta::InSeconds() const { ...@@ -87,6 +107,10 @@ int64_t TimeDelta::InSeconds() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max(); return std::numeric_limits<int64_t>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerSecond; return delta_ / Time::kMicrosecondsPerSecond;
} }
...@@ -95,6 +119,10 @@ double TimeDelta::InMillisecondsF() const { ...@@ -95,6 +119,10 @@ double TimeDelta::InMillisecondsF() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond; return static_cast<double>(delta_) / Time::kMicrosecondsPerMillisecond;
} }
...@@ -103,6 +131,10 @@ int64_t TimeDelta::InMilliseconds() const { ...@@ -103,6 +131,10 @@ int64_t TimeDelta::InMilliseconds() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max(); return std::numeric_limits<int64_t>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ / Time::kMicrosecondsPerMillisecond; return delta_ / Time::kMicrosecondsPerMillisecond;
} }
...@@ -111,6 +143,10 @@ int64_t TimeDelta::InMillisecondsRoundedUp() const { ...@@ -111,6 +143,10 @@ int64_t TimeDelta::InMillisecondsRoundedUp() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max(); return std::numeric_limits<int64_t>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
int64_t result = delta_ / Time::kMicrosecondsPerMillisecond; int64_t result = delta_ / Time::kMicrosecondsPerMillisecond;
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerMillisecond); int64_t remainder = delta_ - (result * Time::kMicrosecondsPerMillisecond);
if (remainder > 0) { if (remainder > 0) {
...@@ -124,6 +160,10 @@ double TimeDelta::InMicrosecondsF() const { ...@@ -124,6 +160,10 @@ double TimeDelta::InMicrosecondsF() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return static_cast<double>(delta_); return static_cast<double>(delta_);
} }
...@@ -132,6 +172,10 @@ int64_t TimeDelta::InNanoseconds() const { ...@@ -132,6 +172,10 @@ int64_t TimeDelta::InNanoseconds() const {
// Preserve max to prevent overflow. // Preserve max to prevent overflow.
return std::numeric_limits<int64_t>::max(); return std::numeric_limits<int64_t>::max();
} }
if (is_min()) {
// Preserve min to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return delta_ * Time::kNanosecondsPerMicrosecond; return delta_ * Time::kNanosecondsPerMicrosecond;
} }
...@@ -177,6 +221,10 @@ time_t Time::ToTimeT() const { ...@@ -177,6 +221,10 @@ time_t Time::ToTimeT() const {
// Preserve max without offset to prevent overflow. // Preserve max without offset to prevent overflow.
return std::numeric_limits<time_t>::max(); return std::numeric_limits<time_t>::max();
} }
if (is_min()) {
// Preserve min without offset to prevent underflow.
return std::numeric_limits<time_t>::min();
}
if (std::numeric_limits<int64_t>::max() - kTimeTToMicrosecondsOffset <= us_) { if (std::numeric_limits<int64_t>::max() - kTimeTToMicrosecondsOffset <= us_) {
DLOG(WARNING) << "Overflow when converting base::Time with internal " << DLOG(WARNING) << "Overflow when converting base::Time with internal " <<
"value " << us_ << " to time_t."; "value " << us_ << " to time_t.";
...@@ -199,6 +247,10 @@ double Time::ToDoubleT() const { ...@@ -199,6 +247,10 @@ double Time::ToDoubleT() const {
// Preserve max without offset to prevent overflow. // Preserve max without offset to prevent overflow.
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
if (is_min()) {
// Preserve min without offset to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
static_cast<double>(kMicrosecondsPerSecond)); static_cast<double>(kMicrosecondsPerSecond));
} }
...@@ -233,6 +285,10 @@ double Time::ToJsTimeIgnoringNull() const { ...@@ -233,6 +285,10 @@ double Time::ToJsTimeIgnoringNull() const {
// Preserve max without offset to prevent overflow. // Preserve max without offset to prevent overflow.
return std::numeric_limits<double>::infinity(); return std::numeric_limits<double>::infinity();
} }
if (is_min()) {
// Preserve min without offset to prevent underflow.
return -std::numeric_limits<double>::infinity();
}
return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) / return (static_cast<double>(us_ - kTimeTToMicrosecondsOffset) /
kMicrosecondsPerMillisecond); kMicrosecondsPerMillisecond);
} }
...@@ -251,6 +307,10 @@ int64_t Time::ToJavaTime() const { ...@@ -251,6 +307,10 @@ int64_t Time::ToJavaTime() const {
// Preserve max without offset to prevent overflow. // Preserve max without offset to prevent overflow.
return std::numeric_limits<int64_t>::max(); return std::numeric_limits<int64_t>::max();
} }
if (is_min()) {
// Preserve min without offset to prevent underflow.
return std::numeric_limits<int64_t>::min();
}
return ((us_ - kTimeTToMicrosecondsOffset) / return ((us_ - kTimeTToMicrosecondsOffset) /
kMicrosecondsPerMillisecond); kMicrosecondsPerMillisecond);
} }
......
...@@ -121,9 +121,11 @@ class BASE_EXPORT TimeDelta { ...@@ -121,9 +121,11 @@ class BASE_EXPORT TimeDelta {
constexpr TimeDelta() : delta_(0) {} constexpr TimeDelta() : delta_(0) {}
// Converts units of time to TimeDeltas. // Converts units of time to TimeDeltas.
// WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may // These conversions treat minimum argument values as min type values or -inf,
// not precisely equal |t|. Hence, floating point values should not be used // and maximum ones as max type values or +inf; and their results will produce
// for storage. // an is_min() or is_max() TimeDelta. WARNING: Floating point arithmetic is
// such that FromXXXD(t.InXXXF()) may not precisely equal |t|. Hence, floating
// point values should not be used for storage.
static constexpr TimeDelta FromDays(int days); static constexpr TimeDelta FromDays(int days);
static constexpr TimeDelta FromHours(int hours); static constexpr TimeDelta FromHours(int hours);
static constexpr TimeDelta FromMinutes(int minutes); static constexpr TimeDelta FromMinutes(int minutes);
...@@ -206,14 +208,16 @@ class BASE_EXPORT TimeDelta { ...@@ -206,14 +208,16 @@ class BASE_EXPORT TimeDelta {
ABI::Windows::Foundation::DateTime ToWinrtDateTime() const; ABI::Windows::Foundation::DateTime ToWinrtDateTime() const;
#endif #endif
// Returns the time delta in some unit. The InXYZF versions return a floating // Returns the time delta in some unit. Minimum argument values return as
// -inf for doubles and min type values otherwise. Maximum ones are treated as
// +inf for doubles and max type values otherwise. Their results will produce
// an is_min() or is_max() TimeDelta. The InXYZF versions return a floating
// point value. The InXYZ versions return a truncated value (aka rounded // point value. The InXYZ versions return a truncated value (aka rounded
// towards zero, std::trunc() behavior). The InXYZFloored() versions round to // towards zero, std::trunc() behavior). The InXYZFloored() versions round to
// lesser integers (std::floor() behavior). The XYZRoundedUp() versions round // lesser integers (std::floor() behavior). The XYZRoundedUp() versions round
// up to greater integers (std::ceil() behavior). // up to greater integers (std::ceil() behavior). WARNING: Floating point
// WARNING: Floating point arithmetic is such that FromXXXD(t.InXXXF()) may // arithmetic is such that FromXXXD(t.InXXXF()) may not precisely equal |t|.
// not precisely equal |t|. Hence, floating point values should not be used // Hence, floating point values should not be used for storage.
// for storage.
int InDays() const; int InDays() const;
int InDaysFloored() const; int InDaysFloored() const;
int InHours() const; int InHours() const;
...@@ -241,7 +245,15 @@ class BASE_EXPORT TimeDelta { ...@@ -241,7 +245,15 @@ class BASE_EXPORT TimeDelta {
constexpr TimeDelta& operator-=(TimeDelta other) { constexpr TimeDelta& operator-=(TimeDelta other) {
return *this = (*this - other); return *this = (*this - other);
} }
constexpr TimeDelta operator-() const { return TimeDelta(-delta_); } constexpr TimeDelta operator-() const {
if (is_max()) {
return Min();
}
if (is_min()) {
return Max();
}
return TimeDelta(-delta_);
}
// Computations with numeric types. // Computations with numeric types.
template <typename T> template <typename T>
...@@ -276,9 +288,33 @@ class BASE_EXPORT TimeDelta { ...@@ -276,9 +288,33 @@ class BASE_EXPORT TimeDelta {
return *this = (*this / a); return *this = (*this / a);
} }
constexpr int64_t operator/(TimeDelta a) const { return delta_ / a.delta_; } constexpr int64_t operator/(TimeDelta a) const {
if (a.delta_ == 0) {
return delta_ < 0 ? std::numeric_limits<int64_t>::min()
: std::numeric_limits<int64_t>::max();
}
if (is_max()) {
if (a.delta_ < 0) {
return std::numeric_limits<int64_t>::min();
}
return std::numeric_limits<int64_t>::max();
}
if (is_min()) {
if (a.delta_ > 0) {
return std::numeric_limits<int64_t>::min();
}
return std::numeric_limits<int64_t>::max();
}
if (a.is_max()) {
return 0;
}
return delta_ / a.delta_;
}
constexpr TimeDelta operator%(TimeDelta a) const { constexpr TimeDelta operator%(TimeDelta a) const {
if (a.is_min() || a.is_max()) {
return TimeDelta(delta_);
}
return TimeDelta(delta_ % a.delta_); return TimeDelta(delta_ % a.delta_);
} }
TimeDelta& operator%=(TimeDelta other) { return *this = (*this % other); } TimeDelta& operator%=(TimeDelta other) { return *this = (*this % other); }
......
...@@ -1529,6 +1529,20 @@ TEST(TimeDelta, MaxConversions) { ...@@ -1529,6 +1529,20 @@ TEST(TimeDelta, MaxConversions) {
""); "");
} }
TEST(TimeDelta, MinConversions) {
constexpr TimeDelta kMin = TimeDelta::Min();
EXPECT_EQ(kMin.InDays(), std::numeric_limits<int>::min());
EXPECT_EQ(kMin.InHours(), std::numeric_limits<int>::min());
EXPECT_EQ(kMin.InMinutes(), std::numeric_limits<int>::min());
EXPECT_EQ(kMin.InSecondsF(), -std::numeric_limits<double>::infinity());
EXPECT_EQ(kMin.InSeconds(), std::numeric_limits<int64_t>::min());
EXPECT_EQ(kMin.InMillisecondsF(), -std::numeric_limits<double>::infinity());
EXPECT_EQ(kMin.InMilliseconds(), std::numeric_limits<int64_t>::min());
EXPECT_EQ(kMin.InMillisecondsRoundedUp(),
std::numeric_limits<int64_t>::min());
}
TEST(TimeDelta, NumericOperators) { TEST(TimeDelta, NumericOperators) {
constexpr double d = 0.5; constexpr double d = 0.5;
EXPECT_EQ(TimeDelta::FromMilliseconds(500), EXPECT_EQ(TimeDelta::FromMilliseconds(500),
...@@ -1634,9 +1648,14 @@ TEST(TimeDelta, Overflows) { ...@@ -1634,9 +1648,14 @@ TEST(TimeDelta, Overflows) {
// evaluation at the same time. // evaluation at the same time.
static_assert(TimeDelta::Max().is_max(), ""); static_assert(TimeDelta::Max().is_max(), "");
static_assert(-TimeDelta::Max() < TimeDelta(), ""); static_assert(-TimeDelta::Max() < TimeDelta(), "");
static_assert(-TimeDelta::Max() > TimeDelta::Min(), ""); static_assert(-TimeDelta::Max() == TimeDelta::Min(), "");
static_assert(TimeDelta() > -TimeDelta::Max(), ""); static_assert(TimeDelta() > -TimeDelta::Max(), "");
static_assert(TimeDelta::Min().is_min(), "");
static_assert(-TimeDelta::Min() > TimeDelta(), "");
static_assert(-TimeDelta::Min() == TimeDelta::Max(), "");
static_assert(TimeDelta() < -TimeDelta::Min(), "");
TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1); TimeDelta large_delta = TimeDelta::Max() - TimeDelta::FromMilliseconds(1);
TimeDelta large_negative = -large_delta; TimeDelta large_negative = -large_delta;
EXPECT_GT(TimeDelta(), large_negative); EXPECT_GT(TimeDelta(), large_negative);
...@@ -1654,6 +1673,26 @@ TEST(TimeDelta, Overflows) { ...@@ -1654,6 +1673,26 @@ TEST(TimeDelta, Overflows) {
EXPECT_TRUE((large_delta / 0.5).is_max()); EXPECT_TRUE((large_delta / 0.5).is_max());
EXPECT_TRUE((large_delta / -0.5).is_min()); EXPECT_TRUE((large_delta / -0.5).is_min());
EXPECT_EQ(TimeDelta::FromSeconds(1) / TimeDelta::FromSeconds(0),
std::numeric_limits<int64_t>::max());
EXPECT_EQ(TimeDelta::Max() / TimeDelta::FromSeconds(10),
std::numeric_limits<int64_t>::max());
EXPECT_EQ(TimeDelta::Max() / TimeDelta::FromSeconds(-10),
std::numeric_limits<int64_t>::min());
EXPECT_EQ(TimeDelta::Min() / TimeDelta::FromSeconds(10),
std::numeric_limits<int64_t>::min());
EXPECT_EQ(TimeDelta::Min() / TimeDelta::FromSeconds(-10),
std::numeric_limits<int64_t>::max());
EXPECT_EQ(TimeDelta::FromSeconds(10) / TimeDelta::Min(), 0);
EXPECT_EQ(TimeDelta::FromSeconds(10) / TimeDelta::Max(), 0);
EXPECT_EQ(TimeDelta::FromSeconds(10) % TimeDelta::Min(),
TimeDelta::FromSeconds(10));
EXPECT_EQ(TimeDelta::FromSeconds(10) % TimeDelta::Max(),
TimeDelta::FromSeconds(10));
// Test that double conversions overflow to infinity. // Test that double conversions overflow to infinity.
EXPECT_EQ((large_delta + kOneSecond).InSecondsF(), EXPECT_EQ((large_delta + kOneSecond).InSecondsF(),
std::numeric_limits<double>::infinity()); std::numeric_limits<double>::infinity());
......
...@@ -24,7 +24,7 @@ void RetryTimer::Start( ...@@ -24,7 +24,7 @@ void RetryTimer::Start(
if (max_wait_time <= base::TimeDelta::FromSeconds(0)) { if (max_wait_time <= base::TimeDelta::FromSeconds(0)) {
remaining_attempts_ = 1; remaining_attempts_ = 1;
} else { } else {
remaining_attempts_ = 1 + max_wait_time / period_; remaining_attempts_ = (max_wait_time + period_) / period_;
} }
DCHECK_GE(remaining_attempts_, 1); DCHECK_GE(remaining_attempts_, 1);
RunTask(); RunTask();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment