From 942b360d820f452707b5a496e3ee73ff48c88b1d Mon Sep 17 00:00:00 2001 From: Sebastian Jansson Date: Wed, 30 May 2018 15:47:44 +0200 Subject: [PATCH] Add conversions to and from double for units. Bug: webrtc:8415 Change-Id: I6b1f7afb163daa327e45c51f1a3fb7cafbb1444e Reviewed-on: https://webrtc-review.googlesource.com/78183 Commit-Queue: Sebastian Jansson Reviewed-by: Karl Wiberg Cr-Commit-Position: refs/heads/master@{#23451} --- api/units/data_rate.h | 87 ++++++++++++++++----- api/units/data_rate_unittest.cc | 44 ++++++++--- api/units/data_size.h | 47 ++++++++++-- api/units/data_size_unittest.cc | 18 +++-- api/units/time_delta.h | 125 +++++++++++++++++++++++++------ api/units/time_delta_unittest.cc | 48 ++++++++++++ api/units/timestamp.cc | 8 -- api/units/timestamp.h | 101 +++++++++++++++++++++---- api/units/timestamp_unittest.cc | 33 ++++++++ 9 files changed, 424 insertions(+), 87 deletions(-) diff --git a/api/units/data_rate.h b/api/units/data_rate.h index 067b200105..c7164e3446 100644 --- a/api/units/data_rate.h +++ b/api/units/data_rate.h @@ -16,6 +16,7 @@ #include #include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" #include "api/units/data_size.h" #include "api/units/time_delta.h" @@ -43,28 +44,78 @@ class DataRate { static DataRate Infinity() { return DataRate(data_rate_impl::kPlusInfinityVal); } - static DataRate bits_per_second(int64_t bits_per_sec) { - RTC_DCHECK_GE(bits_per_sec, 0); - return DataRate(bits_per_sec); + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static DataRate bps(T bits_per_second) { + RTC_DCHECK_GE(bits_per_second, 0); + RTC_DCHECK_LT(bits_per_second, data_rate_impl::kPlusInfinityVal); + return DataRate(rtc::dchecked_cast(bits_per_second)); } - static DataRate bps(int64_t bits_per_sec) { - return DataRate::bits_per_second(bits_per_sec); + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static DataRate kbps(T kilobits_per_sec) { + RTC_DCHECK_GE(kilobits_per_sec, 0); + RTC_DCHECK_LT(kilobits_per_sec, data_rate_impl::kPlusInfinityVal / 1000); + return DataRate::bps(rtc::dchecked_cast(kilobits_per_sec) * 1000); } - static DataRate kbps(int64_t kilobits_per_sec) { - return DataRate::bits_per_second(kilobits_per_sec * 1000); + + template ::value>::type* = + nullptr> + static DataRate bps(T bits_per_second) { + if (bits_per_second == std::numeric_limits::infinity()) { + return Infinity(); + } else { + RTC_DCHECK(!std::isnan(bits_per_second)); + RTC_DCHECK_GE(bits_per_second, 0); + RTC_DCHECK_LT(bits_per_second, data_rate_impl::kPlusInfinityVal); + return DataRate(rtc::dchecked_cast(bits_per_second)); + } } - int64_t bits_per_second() const { + template ::value>::type* = + nullptr> + static DataRate kbps(T kilobits_per_sec) { + return DataRate::bps(kilobits_per_sec * 1e3); + } + + template + typename std::enable_if::value, T>::type bps() const { RTC_DCHECK(IsFinite()); - return bits_per_sec_; + return rtc::dchecked_cast(bits_per_sec_); } - int64_t bps() const { return bits_per_second(); } - int64_t kbps() const { return (bps() + 500) / 1000; } + template + typename std::enable_if::value, T>::type kbps() const { + return rtc::dchecked_cast((bps() + 500) / 1000); + } + + template + typename std::enable_if::value, T>::type bps() + const { + if (IsInfinite()) { + return std::numeric_limits::infinity(); + } else { + return bits_per_sec_; + } + } + template + typename std::enable_if::value, T>::type kbps() + const { + return bps() * 1e-3; + } + bool IsZero() const { return bits_per_sec_ == 0; } bool IsInfinite() const { return bits_per_sec_ == data_rate_impl::kPlusInfinityVal; } bool IsFinite() const { return !IsInfinite(); } + double operator/(const DataRate& other) const { + return bps() / other.bps(); + } bool operator==(const DataRate& other) const { return bits_per_sec_ == other.bits_per_sec_; } @@ -92,34 +143,32 @@ class DataRate { }; inline DataRate operator*(const DataRate& rate, const double& scalar) { - return DataRate::bits_per_second(std::round(rate.bits_per_second() * scalar)); + return DataRate::bps(std::round(rate.bps() * scalar)); } inline DataRate operator*(const double& scalar, const DataRate& rate) { return rate * scalar; } inline DataRate operator*(const DataRate& rate, const int64_t& scalar) { - return DataRate::bits_per_second(rate.bits_per_second() * scalar); + return DataRate::bps(rate.bps() * scalar); } inline DataRate operator*(const int64_t& scalar, const DataRate& rate) { return rate * scalar; } inline DataRate operator*(const DataRate& rate, const int32_t& scalar) { - return DataRate::bits_per_second(rate.bits_per_second() * scalar); + return DataRate::bps(rate.bps() * scalar); } inline DataRate operator*(const int32_t& scalar, const DataRate& rate) { return rate * scalar; } inline DataRate operator/(const DataSize& size, const TimeDelta& duration) { - return DataRate::bits_per_second(data_rate_impl::Microbits(size) / - duration.us()); + return DataRate::bps(data_rate_impl::Microbits(size) / duration.us()); } inline TimeDelta operator/(const DataSize& size, const DataRate& rate) { - return TimeDelta::us(data_rate_impl::Microbits(size) / - rate.bits_per_second()); + return TimeDelta::us(data_rate_impl::Microbits(size) / rate.bps()); } inline DataSize operator*(const DataRate& rate, const TimeDelta& duration) { - int64_t microbits = rate.bits_per_second() * duration.us(); + int64_t microbits = rate.bps() * duration.us(); return DataSize::bytes((microbits + 4000000) / 8000000); } inline DataSize operator*(const TimeDelta& duration, const DataRate& rate) { diff --git a/api/units/data_rate_unittest.cc b/api/units/data_rate_unittest.cc index d4dd192d37..9a58b47b21 100644 --- a/api/units/data_rate_unittest.cc +++ b/api/units/data_rate_unittest.cc @@ -15,7 +15,6 @@ namespace webrtc { namespace test { TEST(DataRateTest, GetBackSameValues) { const int64_t kValue = 123 * 8; - EXPECT_EQ(DataRate::bits_per_second(kValue).bits_per_second(), kValue); EXPECT_EQ(DataRate::bps(kValue).bps(), kValue); EXPECT_EQ(DataRate::kbps(kValue).kbps(), kValue); } @@ -28,22 +27,22 @@ TEST(DataRateTest, GetDifferentPrefix) { TEST(DataRateTest, IdentityChecks) { const int64_t kValue = 3000; EXPECT_TRUE(DataRate::Zero().IsZero()); - EXPECT_FALSE(DataRate::bits_per_second(kValue).IsZero()); + EXPECT_FALSE(DataRate::bps(kValue).IsZero()); EXPECT_TRUE(DataRate::Infinity().IsInfinite()); EXPECT_FALSE(DataRate::Zero().IsInfinite()); - EXPECT_FALSE(DataRate::bits_per_second(kValue).IsInfinite()); + EXPECT_FALSE(DataRate::bps(kValue).IsInfinite()); EXPECT_FALSE(DataRate::Infinity().IsFinite()); - EXPECT_TRUE(DataRate::bits_per_second(kValue).IsFinite()); + EXPECT_TRUE(DataRate::bps(kValue).IsFinite()); EXPECT_TRUE(DataRate::Zero().IsFinite()); } TEST(DataRateTest, ComparisonOperators) { const int64_t kSmall = 450; const int64_t kLarge = 451; - const DataRate small = DataRate::bits_per_second(kSmall); - const DataRate large = DataRate::bits_per_second(kLarge); + const DataRate small = DataRate::bps(kSmall); + const DataRate large = DataRate::bps(kLarge); EXPECT_EQ(DataRate::Zero(), DataRate::bps(0)); EXPECT_EQ(DataRate::Infinity(), DataRate::Infinity()); @@ -59,15 +58,36 @@ TEST(DataRateTest, ComparisonOperators) { EXPECT_GT(DataRate::Infinity(), large); } +TEST(DataRateTest, ConvertsToAndFromDouble) { + const int64_t kValue = 128; + const double kDoubleValue = static_cast(kValue); + const double kDoubleKbps = kValue * 1e-3; + const double kFloatKbps = static_cast(kDoubleKbps); + + EXPECT_EQ(DataRate::bps(kValue).bps(), kDoubleValue); + EXPECT_EQ(DataRate::bps(kValue).kbps(), kDoubleKbps); + EXPECT_EQ(DataRate::bps(kValue).kbps(), kFloatKbps); + EXPECT_EQ(DataRate::bps(kDoubleValue).bps(), kValue); + EXPECT_EQ(DataRate::kbps(kDoubleKbps).bps(), kValue); + + const double kInfinity = std::numeric_limits::infinity(); + EXPECT_EQ(DataRate::Infinity().bps(), kInfinity); + EXPECT_TRUE(DataRate::bps(kInfinity).IsInfinite()); + EXPECT_TRUE(DataRate::kbps(kInfinity).IsInfinite()); +} + TEST(DataRateTest, MathOperations) { const int64_t kValueA = 450; const int64_t kValueB = 267; - const DataRate size_a = DataRate::bits_per_second(kValueA); + const DataRate rate_a = DataRate::bps(kValueA); + const DataRate rate_b = DataRate::bps(kValueB); const int32_t kInt32Value = 123; const double kFloatValue = 123.0; - EXPECT_EQ((size_a * kValueB).bits_per_second(), kValueA * kValueB); - EXPECT_EQ((size_a * kInt32Value).bits_per_second(), kValueA * kInt32Value); - EXPECT_EQ((size_a * kFloatValue).bits_per_second(), kValueA * kFloatValue); + EXPECT_EQ((rate_a * kValueB).bps(), kValueA * kValueB); + EXPECT_EQ((rate_a * kInt32Value).bps(), kValueA * kInt32Value); + EXPECT_EQ((rate_a * kFloatValue).bps(), kValueA * kFloatValue); + + EXPECT_EQ(rate_a / rate_b, static_cast(kValueA) / kValueB); } TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) { @@ -75,11 +95,11 @@ TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) { const int64_t kBitsPerSecond = 440; const int64_t kBytes = 44000; const TimeDelta delta_a = TimeDelta::seconds(kSeconds); - const DataRate rate_b = DataRate::bits_per_second(kBitsPerSecond); + const DataRate rate_b = DataRate::bps(kBitsPerSecond); const DataSize size_c = DataSize::bytes(kBytes); EXPECT_EQ((delta_a * rate_b).bytes(), kSeconds * kBitsPerSecond / 8); EXPECT_EQ((rate_b * delta_a).bytes(), kSeconds * kBitsPerSecond / 8); - EXPECT_EQ((size_c / delta_a).bits_per_second(), kBytes * 8 / kSeconds); + EXPECT_EQ((size_c / delta_a).bps(), kBytes * 8 / kSeconds); EXPECT_EQ((size_c / rate_b).seconds(), kBytes * 8 / kBitsPerSecond); } diff --git a/api/units/data_size.h b/api/units/data_size.h index 74ab19e664..8c35766b1e 100644 --- a/api/units/data_size.h +++ b/api/units/data_size.h @@ -15,8 +15,10 @@ #include #include #include +#include #include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { namespace data_size_impl { @@ -31,15 +33,46 @@ class DataSize { static DataSize Infinity() { return DataSize(data_size_impl::kPlusInfinityVal); } - static DataSize bytes(int64_t bytes) { + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static DataSize bytes(T bytes) { RTC_DCHECK_GE(bytes, 0); - return DataSize(bytes); + RTC_DCHECK_LT(bytes, data_size_impl::kPlusInfinityVal); + return DataSize(rtc::dchecked_cast(bytes)); } - int64_t bytes() const { + + template ::value>::type* = + nullptr> + static DataSize bytes(T bytes) { + if (bytes == std::numeric_limits::infinity()) { + return Infinity(); + } else { + RTC_DCHECK(!std::isnan(bytes)); + RTC_DCHECK_GE(bytes, 0); + RTC_DCHECK_LT(bytes, data_size_impl::kPlusInfinityVal); + return DataSize(rtc::dchecked_cast(bytes)); + } + } + + template + typename std::enable_if::value, T>::type bytes() const { RTC_DCHECK(IsFinite()); - return bytes_; + return rtc::dchecked_cast(bytes_); } - int64_t kilobytes() const { return (bytes() + 500) / 1000; } + + template + typename std::enable_if::value, T>::type bytes() + const { + if (IsInfinite()) { + return std::numeric_limits::infinity(); + } else { + return bytes_; + } + } + bool IsZero() const { return bytes_ == 0; } bool IsInfinite() const { return bytes_ == data_size_impl::kPlusInfinityVal; } bool IsFinite() const { return !IsInfinite(); } @@ -57,6 +90,9 @@ class DataSize { bytes_ += other.bytes(); return *this; } + double operator/(const DataSize& other) const { + return bytes() / other.bytes(); + } bool operator==(const DataSize& other) const { return bytes_ == other.bytes_; } @@ -76,6 +112,7 @@ class DataSize { explicit DataSize(int64_t bytes) : bytes_(bytes) {} int64_t bytes_; }; + inline DataSize operator*(const DataSize& size, const double& scalar) { return DataSize::bytes(std::round(size.bytes() * scalar)); } diff --git a/api/units/data_size_unittest.cc b/api/units/data_size_unittest.cc index 35cff018ab..7747258dc8 100644 --- a/api/units/data_size_unittest.cc +++ b/api/units/data_size_unittest.cc @@ -19,11 +19,6 @@ TEST(DataSizeTest, GetBackSameValues) { EXPECT_EQ(DataSize::bytes(kValue).bytes(), kValue); } -TEST(DataSizeTest, GetDifferentPrefix) { - const int64_t kValue = 123 * 8000; - EXPECT_EQ(DataSize::bytes(kValue).kilobytes(), kValue / 1000); -} - TEST(DataSizeTest, IdentityChecks) { const int64_t kValue = 3000; EXPECT_TRUE(DataSize::Zero().IsZero()); @@ -58,6 +53,18 @@ TEST(DataSizeTest, ComparisonOperators) { EXPECT_GT(DataSize::Infinity(), large); } +TEST(DataSizeTest, ConvertsToAndFromDouble) { + const int64_t kValue = 128; + const double kDoubleValue = static_cast(kValue); + + EXPECT_EQ(DataSize::bytes(kValue).bytes(), kDoubleValue); + EXPECT_EQ(DataSize::bytes(kDoubleValue).bytes(), kValue); + + const double kInfinity = std::numeric_limits::infinity(); + EXPECT_EQ(DataSize::Infinity().bytes(), kInfinity); + EXPECT_TRUE(DataSize::bytes(kInfinity).IsInfinite()); +} + TEST(DataSizeTest, MathOperations) { const int64_t kValueA = 450; const int64_t kValueB = 267; @@ -73,6 +80,7 @@ TEST(DataSizeTest, MathOperations) { EXPECT_EQ((size_a * kFloatValue).bytes(), kValueA * kFloatValue); EXPECT_EQ((size_a / 10).bytes(), kValueA / 10); + EXPECT_EQ(size_a / size_b, static_cast(kValueA) / kValueB); DataSize mutable_size = DataSize::bytes(kValueA); mutable_size += size_b; diff --git a/api/units/time_delta.h b/api/units/time_delta.h index 2491920cdb..0f99e805af 100644 --- a/api/units/time_delta.h +++ b/api/units/time_delta.h @@ -17,6 +17,7 @@ #include #include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { namespace timedelta_impl { @@ -41,33 +42,107 @@ class TimeDelta { static TimeDelta MinusInfinity() { return TimeDelta(timedelta_impl::kMinusInfinityVal); } - static TimeDelta seconds(int64_t seconds) { - return TimeDelta::us(seconds * 1000000); + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static TimeDelta seconds(T seconds) { + RTC_DCHECK_GT(seconds, timedelta_impl::kMinusInfinityVal / 1000000); + RTC_DCHECK_LT(seconds, timedelta_impl::kPlusInfinityVal / 1000000); + return TimeDelta(rtc::dchecked_cast(seconds) * 1000000); } - static TimeDelta ms(int64_t milliseconds) { - return TimeDelta::us(milliseconds * 1000); + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static TimeDelta ms(T milliseconds) { + RTC_DCHECK_GT(milliseconds, timedelta_impl::kMinusInfinityVal / 1000); + RTC_DCHECK_LT(milliseconds, timedelta_impl::kPlusInfinityVal / 1000); + return TimeDelta(rtc::dchecked_cast(milliseconds) * 1000); } - static TimeDelta us(int64_t microseconds) { - // Infinities only allowed via use of explicit constants. - RTC_DCHECK(microseconds > std::numeric_limits::min()); - RTC_DCHECK(microseconds < std::numeric_limits::max()); - return TimeDelta(microseconds); - } - int64_t seconds() const { - return (us() + (us() >= 0 ? 500000 : -500000)) / 1000000; - } - int64_t ms() const { return (us() + (us() >= 0 ? 500 : -500)) / 1000; } - int64_t us() const { - RTC_DCHECK(IsFinite()); - return microseconds_; - } - int64_t ns() const { - RTC_DCHECK(us() > std::numeric_limits::min() / 1000); - RTC_DCHECK(us() < std::numeric_limits::max() / 1000); - return us() * 1000; + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static TimeDelta us(T microseconds) { + RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal); + RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal); + return TimeDelta(rtc::dchecked_cast(microseconds)); } - double SecondsAsDouble() const; + template ::value>::type* = + nullptr> + static TimeDelta seconds(T seconds) { + return TimeDelta::us(seconds * 1e6); + } + template ::value>::type* = + nullptr> + static TimeDelta ms(T milliseconds) { + return TimeDelta::us(milliseconds * 1e3); + } + template ::value>::type* = + nullptr> + static TimeDelta us(T microseconds) { + if (microseconds == std::numeric_limits::infinity()) { + return PlusInfinity(); + } else if (microseconds == -std::numeric_limits::infinity()) { + return MinusInfinity(); + } else { + RTC_DCHECK(!std::isnan(microseconds)); + RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal); + RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal); + return TimeDelta(rtc::dchecked_cast(microseconds)); + } + } + + template + typename std::enable_if::value, T>::type seconds() const { + return rtc::dchecked_cast((us() + (us() >= 0 ? 500000 : -500000)) / + 1000000); + } + template + typename std::enable_if::value, T>::type ms() const { + return rtc::dchecked_cast((us() + (us() >= 0 ? 500 : -500)) / 1000); + } + template + typename std::enable_if::value, T>::type us() const { + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(microseconds_); + } + template + typename std::enable_if::value, T>::type ns() const { + RTC_DCHECK_GE(us(), std::numeric_limits::min() / 1000); + RTC_DCHECK_LE(us(), std::numeric_limits::max() / 1000); + return rtc::dchecked_cast(us() * 1000); + } + + template + typename std::enable_if::value, T>::type seconds() + const { + return us() * 1e-6; + } + template + typename std::enable_if::value, T>::type ms() + const { + return us() * 1e-3; + } + template + typename std::enable_if::value, T>::type us() + const { + if (IsPlusInfinity()) { + return std::numeric_limits::infinity(); + } else if (IsMinusInfinity()) { + return -std::numeric_limits::infinity(); + } else { + return microseconds_; + } + } + template + typename std::enable_if::value, T>::type ns() + const { + return us() * 1e3; + } TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); } bool IsZero() const { return microseconds_ == 0; } @@ -96,7 +171,9 @@ class TimeDelta { microseconds_ += other.us(); return *this; } - + double operator/(const TimeDelta& other) const { + return us() / other.us(); + } bool operator==(const TimeDelta& other) const { return microseconds_ == other.microseconds_; } diff --git a/api/units/time_delta_unittest.cc b/api/units/time_delta_unittest.cc index 493c6bf4ba..94a27f2383 100644 --- a/api/units/time_delta_unittest.cc +++ b/api/units/time_delta_unittest.cc @@ -80,6 +80,51 @@ TEST(TimeDeltaTest, ComparisonOperators) { EXPECT_LT(TimeDelta::MinusInfinity(), TimeDelta::Zero()); } +TEST(TimeDeltaTest, CanBeInititializedFromLargeInt) { + const int kMaxInt = std::numeric_limits::max(); + EXPECT_EQ(TimeDelta::seconds(kMaxInt).us(), + static_cast(kMaxInt) * 1000000); + EXPECT_EQ(TimeDelta::ms(kMaxInt).us(), static_cast(kMaxInt) * 1000); +} + +TEST(TimeDeltaTest, ConvertsToAndFromDouble) { + const int64_t kMicros = 17017; + const double kNanosDouble = kMicros * 1e3; + const double kMicrosDouble = kMicros; + const double kMillisDouble = kMicros * 1e-3; + const double kSecondsDouble = kMillisDouble * 1e-3; + + EXPECT_EQ(TimeDelta::us(kMicros).seconds(), kSecondsDouble); + EXPECT_EQ(TimeDelta::seconds(kSecondsDouble).us(), kMicros); + + EXPECT_EQ(TimeDelta::us(kMicros).ms(), kMillisDouble); + EXPECT_EQ(TimeDelta::ms(kMillisDouble).us(), kMicros); + + EXPECT_EQ(TimeDelta::us(kMicros).us(), kMicrosDouble); + EXPECT_EQ(TimeDelta::us(kMicrosDouble).us(), kMicros); + + EXPECT_NEAR(TimeDelta::us(kMicros).ns(), kNanosDouble, 1); + + const double kPlusInfinity = std::numeric_limits::infinity(); + const double kMinusInfinity = -kPlusInfinity; + + EXPECT_EQ(TimeDelta::PlusInfinity().seconds(), kPlusInfinity); + EXPECT_EQ(TimeDelta::MinusInfinity().seconds(), kMinusInfinity); + EXPECT_EQ(TimeDelta::PlusInfinity().ms(), kPlusInfinity); + EXPECT_EQ(TimeDelta::MinusInfinity().ms(), kMinusInfinity); + EXPECT_EQ(TimeDelta::PlusInfinity().us(), kPlusInfinity); + EXPECT_EQ(TimeDelta::MinusInfinity().us(), kMinusInfinity); + EXPECT_EQ(TimeDelta::PlusInfinity().ns(), kPlusInfinity); + EXPECT_EQ(TimeDelta::MinusInfinity().ns(), kMinusInfinity); + + EXPECT_TRUE(TimeDelta::seconds(kPlusInfinity).IsPlusInfinity()); + EXPECT_TRUE(TimeDelta::seconds(kMinusInfinity).IsMinusInfinity()); + EXPECT_TRUE(TimeDelta::ms(kPlusInfinity).IsPlusInfinity()); + EXPECT_TRUE(TimeDelta::ms(kMinusInfinity).IsMinusInfinity()); + EXPECT_TRUE(TimeDelta::us(kPlusInfinity).IsPlusInfinity()); + EXPECT_TRUE(TimeDelta::us(kMinusInfinity).IsMinusInfinity()); +} + TEST(TimeDeltaTest, MathOperations) { const int64_t kValueA = 267; const int64_t kValueB = 450; @@ -94,6 +139,9 @@ TEST(TimeDeltaTest, MathOperations) { EXPECT_EQ((TimeDelta::us(kValueA) * kInt32Value).us(), kValueA * kInt32Value); EXPECT_EQ((TimeDelta::us(kValueA) * kFloatValue).us(), kValueA * kFloatValue); + EXPECT_EQ((delta_b / 10).ms(), kValueB / 10); + EXPECT_EQ(delta_b / delta_a, static_cast(kValueB) / kValueA); + EXPECT_EQ(TimeDelta::us(-kValueA).Abs().us(), kValueA); EXPECT_EQ(TimeDelta::us(kValueA).Abs().us(), kValueA); } diff --git a/api/units/timestamp.cc b/api/units/timestamp.cc index 7ae084ca29..4b2c44b9a7 100644 --- a/api/units/timestamp.cc +++ b/api/units/timestamp.cc @@ -13,14 +13,6 @@ #include "rtc_base/strings/string_builder.h" namespace webrtc { -double Timestamp::SecondsAsDouble() const { - if (IsInfinite()) { - return std::numeric_limits::infinity(); - } else { - return us() * 1e-6; - } -} - std::string ToString(const Timestamp& value) { char buf[64]; rtc::SimpleStringBuilder sb(buf); diff --git a/api/units/timestamp.h b/api/units/timestamp.h index 40f3e922ef..a66e061fb9 100644 --- a/api/units/timestamp.h +++ b/api/units/timestamp.h @@ -17,6 +17,7 @@ #include "api/units/time_delta.h" #include "rtc_base/checks.h" +#include "rtc_base/numerics/safe_conversions.h" namespace webrtc { namespace timestamp_impl { @@ -34,22 +35,94 @@ class Timestamp { static Timestamp Infinity() { return Timestamp(timestamp_impl::kPlusInfinityVal); } - static Timestamp seconds(int64_t seconds) { - return Timestamp::us(seconds * 1000000); - } - static Timestamp ms(int64_t millis) { return Timestamp::us(millis * 1000); } - static Timestamp us(int64_t micros) { - RTC_DCHECK_GE(micros, 0); - return Timestamp(micros); - } - int64_t seconds() const { return (us() + 500000) / 1000000; } - int64_t ms() const { return (us() + 500) / 1000; } - int64_t us() const { - RTC_DCHECK(IsFinite()); - return microseconds_; + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static Timestamp seconds(T seconds) { + RTC_DCHECK_GE(seconds, 0); + RTC_DCHECK_LT(seconds, timestamp_impl::kPlusInfinityVal / 1000000); + return Timestamp(rtc::dchecked_cast(seconds) * 1000000); } - double SecondsAsDouble() const; + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static Timestamp ms(T milliseconds) { + RTC_DCHECK_GE(milliseconds, 0); + RTC_DCHECK_LT(milliseconds, timestamp_impl::kPlusInfinityVal / 1000); + return Timestamp(rtc::dchecked_cast(milliseconds) * 1000); + } + + template < + typename T, + typename std::enable_if::value>::type* = nullptr> + static Timestamp us(T microseconds) { + RTC_DCHECK_GE(microseconds, 0); + RTC_DCHECK_LT(microseconds, timestamp_impl::kPlusInfinityVal); + return Timestamp(rtc::dchecked_cast(microseconds)); + } + + template ::value>::type* = + nullptr> + static Timestamp seconds(T seconds) { + return Timestamp::us(seconds * 1e6); + } + + template ::value>::type* = + nullptr> + static Timestamp ms(T milliseconds) { + return Timestamp::us(milliseconds * 1e3); + } + template ::value>::type* = + nullptr> + static Timestamp us(T microseconds) { + if (microseconds == std::numeric_limits::infinity()) { + return Infinity(); + } else { + RTC_DCHECK(!std::isnan(microseconds)); + RTC_DCHECK_GE(microseconds, 0); + RTC_DCHECK_LT(microseconds, timestamp_impl::kPlusInfinityVal); + return Timestamp(rtc::dchecked_cast(microseconds)); + } + } + + template + typename std::enable_if::value, T>::type seconds() const { + return rtc::dchecked_cast((us() + 500000) / 1000000); + } + template + typename std::enable_if::value, T>::type ms() const { + return rtc::dchecked_cast((us() + 500) / 1000); + } + template + typename std::enable_if::value, T>::type us() const { + RTC_DCHECK(IsFinite()); + return rtc::dchecked_cast(microseconds_); + } + + template + typename std::enable_if::value, T>::type seconds() + const { + return us() * 1e-6; + } + template + typename std::enable_if::value, T>::type ms() + const { + return us() * 1e-3; + } + template + typename std::enable_if::value, T>::type us() + const { + if (IsInfinite()) { + return std::numeric_limits::infinity(); + } else { + return microseconds_; + } + } bool IsInfinite() const { return microseconds_ == timestamp_impl::kPlusInfinityVal; diff --git a/api/units/timestamp_unittest.cc b/api/units/timestamp_unittest.cc index 3b2770ceb3..eecfe02a9a 100644 --- a/api/units/timestamp_unittest.cc +++ b/api/units/timestamp_unittest.cc @@ -58,6 +58,39 @@ TEST(TimestampTest, ComparisonOperators) { EXPECT_GT(Timestamp::ms(kLarge), Timestamp::ms(kSmall)); } +TEST(TimestampTest, CanBeInititializedFromLargeInt) { + const int kMaxInt = std::numeric_limits::max(); + EXPECT_EQ(Timestamp::seconds(kMaxInt).us(), + static_cast(kMaxInt) * 1000000); + EXPECT_EQ(Timestamp::ms(kMaxInt).us(), static_cast(kMaxInt) * 1000); +} + +TEST(TimestampTest, ConvertsToAndFromDouble) { + const int64_t kMicros = 17017; + const double kMicrosDouble = kMicros; + const double kMillisDouble = kMicros * 1e-3; + const double kSecondsDouble = kMillisDouble * 1e-3; + + EXPECT_EQ(Timestamp::us(kMicros).seconds(), kSecondsDouble); + EXPECT_EQ(Timestamp::seconds(kSecondsDouble).us(), kMicros); + + EXPECT_EQ(Timestamp::us(kMicros).ms(), kMillisDouble); + EXPECT_EQ(Timestamp::ms(kMillisDouble).us(), kMicros); + + EXPECT_EQ(Timestamp::us(kMicros).us(), kMicrosDouble); + EXPECT_EQ(Timestamp::us(kMicrosDouble).us(), kMicros); + + const double kPlusInfinity = std::numeric_limits::infinity(); + + EXPECT_EQ(Timestamp::Infinity().seconds(), kPlusInfinity); + EXPECT_EQ(Timestamp::Infinity().ms(), kPlusInfinity); + EXPECT_EQ(Timestamp::Infinity().us(), kPlusInfinity); + + EXPECT_TRUE(Timestamp::seconds(kPlusInfinity).IsInfinite()); + EXPECT_TRUE(Timestamp::ms(kPlusInfinity).IsInfinite()); + EXPECT_TRUE(Timestamp::us(kPlusInfinity).IsInfinite()); +} + TEST(UnitConversionTest, TimestampAndTimeDeltaMath) { const int64_t kValueA = 267; const int64_t kValueB = 450;