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 {
// Preserve max to prevent overflow.
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);
}
......@@ -50,6 +54,10 @@ int TimeDelta::InDaysFloored() const {
// Preserve max to prevent overflow.
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;
int64_t remainder = delta_ - (result * Time::kMicrosecondsPerDay);
if (remainder < 0) {
......@@ -63,6 +71,10 @@ int TimeDelta::InHours() const {
// Preserve max to prevent overflow.
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);
}
......@@ -71,6 +83,10 @@ int TimeDelta::InMinutes() const {
// Preserve max to prevent overflow.
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);
}
......@@ -79,6 +95,10 @@ double TimeDelta::InSecondsF() const {
// Preserve max to prevent overflow.
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;
}
......@@ -87,6 +107,10 @@ int64_t TimeDelta::InSeconds() const {
// Preserve max to prevent overflow.
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;
}
......@@ -95,6 +119,10 @@ double TimeDelta::InMillisecondsF() const {
// Preserve max to prevent overflow.
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;
}
......@@ -103,6 +131,10 @@ int64_t TimeDelta::InMilliseconds() const {
// Preserve max to prevent overflow.
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;
}
......@@ -111,6 +143,10 @@ int64_t TimeDelta::InMillisecondsRoundedUp() const {
// Preserve max to prevent overflow.
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 remainder = delta_ - (result * Time::kMicrosecondsPerMillisecond);
if (remainder > 0) {
......@@ -124,6 +160,10 @@ double TimeDelta::InMicrosecondsF() const {
// Preserve max to prevent overflow.
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_);
}
......@@ -132,6 +172,10 @@ int64_t TimeDelta::InNanoseconds() const {
// Preserve max to prevent overflow.
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;
}
......@@ -177,6 +221,10 @@ time_t Time::ToTimeT() const {
// Preserve max without offset to prevent overflow.
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_) {
DLOG(WARNING) << "Overflow when converting base::Time with internal " <<
"value " << us_ << " to time_t.";
......@@ -199,6 +247,10 @@ double Time::ToDoubleT() const {
// Preserve max without offset to prevent overflow.
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) /
static_cast<double>(kMicrosecondsPerSecond));
}
......@@ -233,6 +285,10 @@ double Time::ToJsTimeIgnoringNull() const {
// Preserve max without offset to prevent overflow.
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) /
kMicrosecondsPerMillisecond);
}
......@@ -251,6 +307,10 @@ int64_t Time::ToJavaTime() const {
// Preserve max without offset to prevent overflow.
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) /
kMicrosecondsPerMillisecond);
}
......
......@@ -121,9 +121,11 @@ class BASE_EXPORT TimeDelta {
constexpr TimeDelta() : delta_(0) {}
// Converts units of time to TimeDeltas.
// 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.
// These conversions treat minimum argument values as min type values or -inf,
// and maximum ones as max type values or +inf; and their results will produce
// 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 FromHours(int hours);
static constexpr TimeDelta FromMinutes(int minutes);
......@@ -206,14 +208,16 @@ class BASE_EXPORT TimeDelta {
ABI::Windows::Foundation::DateTime ToWinrtDateTime() const;
#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
// towards zero, std::trunc() behavior). The InXYZFloored() versions round to
// lesser integers (std::floor() behavior). The XYZRoundedUp() versions round
// up to greater integers (std::ceil() behavior).
// 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.
// up to greater integers (std::ceil() behavior). 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.
int InDays() const;
int InDaysFloored() const;
int InHours() const;
......@@ -241,7 +245,15 @@ class BASE_EXPORT TimeDelta {
constexpr TimeDelta& operator-=(TimeDelta 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.
template <typename T>
......@@ -276,9 +288,33 @@ class BASE_EXPORT TimeDelta {
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 {
if (a.is_min() || a.is_max()) {
return TimeDelta(delta_);
}
return TimeDelta(delta_ % a.delta_);
}
TimeDelta& operator%=(TimeDelta other) { return *this = (*this % other); }
......
......@@ -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) {
constexpr double d = 0.5;
EXPECT_EQ(TimeDelta::FromMilliseconds(500),
......@@ -1634,9 +1648,14 @@ TEST(TimeDelta, Overflows) {
// evaluation at the same time.
static_assert(TimeDelta::Max().is_max(), "");
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::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_negative = -large_delta;
EXPECT_GT(TimeDelta(), large_negative);
......@@ -1654,6 +1673,26 @@ TEST(TimeDelta, Overflows) {
EXPECT_TRUE((large_delta / 0.5).is_max());
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.
EXPECT_EQ((large_delta + kOneSecond).InSecondsF(),
std::numeric_limits<double>::infinity());
......
......@@ -24,7 +24,7 @@ void RetryTimer::Start(
if (max_wait_time <= base::TimeDelta::FromSeconds(0)) {
remaining_attempts_ = 1;
} else {
remaining_attempts_ = 1 + max_wait_time / period_;
remaining_attempts_ = (max_wait_time + period_) / period_;
}
DCHECK_GE(remaining_attempts_, 1);
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