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 <srte@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23451}
diff --git a/api/units/data_rate.h b/api/units/data_rate.h
index 067b200..c7164e3 100644
--- a/api/units/data_rate.h
+++ b/api/units/data_rate.h
@@ -16,6 +16,7 @@
#include <string>
#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 @@
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<std::is_integral<T>::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<int64_t>(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<std::is_integral<T>::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<int64_t>(kilobits_per_sec) * 1000);
}
- static DataRate kbps(int64_t kilobits_per_sec) {
- return DataRate::bits_per_second(kilobits_per_sec * 1000);
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static DataRate bps(T bits_per_second) {
+ if (bits_per_second == std::numeric_limits<T>::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<int64_t>(bits_per_second));
+ }
}
- int64_t bits_per_second() const {
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static DataRate kbps(T kilobits_per_sec) {
+ return DataRate::bps(kilobits_per_sec * 1e3);
+ }
+
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type bps() const {
RTC_DCHECK(IsFinite());
- return bits_per_sec_;
+ return rtc::dchecked_cast<T>(bits_per_sec_);
}
- int64_t bps() const { return bits_per_second(); }
- int64_t kbps() const { return (bps() + 500) / 1000; }
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type kbps() const {
+ return rtc::dchecked_cast<T>((bps() + 500) / 1000);
+ }
+
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type bps()
+ const {
+ if (IsInfinite()) {
+ return std::numeric_limits<T>::infinity();
+ } else {
+ return bits_per_sec_;
+ }
+ }
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type kbps()
+ const {
+ return bps<T>() * 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<double>() / other.bps<double>();
+ }
bool operator==(const DataRate& other) const {
return bits_per_sec_ == other.bits_per_sec_;
}
@@ -92,34 +143,32 @@
};
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 d4dd192..9a58b47 100644
--- a/api/units/data_rate_unittest.cc
+++ b/api/units/data_rate_unittest.cc
@@ -15,7 +15,6 @@
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, 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 @@
EXPECT_GT(DataRate::Infinity(), large);
}
+TEST(DataRateTest, ConvertsToAndFromDouble) {
+ const int64_t kValue = 128;
+ const double kDoubleValue = static_cast<double>(kValue);
+ const double kDoubleKbps = kValue * 1e-3;
+ const double kFloatKbps = static_cast<float>(kDoubleKbps);
+
+ EXPECT_EQ(DataRate::bps(kValue).bps<double>(), kDoubleValue);
+ EXPECT_EQ(DataRate::bps(kValue).kbps<double>(), kDoubleKbps);
+ EXPECT_EQ(DataRate::bps(kValue).kbps<float>(), kFloatKbps);
+ EXPECT_EQ(DataRate::bps(kDoubleValue).bps(), kValue);
+ EXPECT_EQ(DataRate::kbps(kDoubleKbps).bps(), kValue);
+
+ const double kInfinity = std::numeric_limits<double>::infinity();
+ EXPECT_EQ(DataRate::Infinity().bps<double>(), 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<double>(kValueA) / kValueB);
}
TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) {
@@ -75,11 +95,11 @@
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 74ab19e..8c35766 100644
--- a/api/units/data_size.h
+++ b/api/units/data_size.h
@@ -15,8 +15,10 @@
#include <cmath>
#include <limits>
#include <string>
+#include <type_traits>
#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace data_size_impl {
@@ -31,15 +33,46 @@
static DataSize Infinity() {
return DataSize(data_size_impl::kPlusInfinityVal);
}
- static DataSize bytes(int64_t bytes) {
+
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::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<int64_t>(bytes));
}
- int64_t bytes() const {
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static DataSize bytes(T bytes) {
+ if (bytes == std::numeric_limits<T>::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<int64_t>(bytes));
+ }
+ }
+
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type bytes() const {
RTC_DCHECK(IsFinite());
- return bytes_;
+ return rtc::dchecked_cast<T>(bytes_);
}
- int64_t kilobytes() const { return (bytes() + 500) / 1000; }
+
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type bytes()
+ const {
+ if (IsInfinite()) {
+ return std::numeric_limits<T>::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 @@
bytes_ += other.bytes();
return *this;
}
+ double operator/(const DataSize& other) const {
+ return bytes<double>() / other.bytes<double>();
+ }
bool operator==(const DataSize& other) const {
return bytes_ == other.bytes_;
}
@@ -76,6 +112,7 @@
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 35cff01..7747258 100644
--- a/api/units/data_size_unittest.cc
+++ b/api/units/data_size_unittest.cc
@@ -19,11 +19,6 @@
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 @@
EXPECT_GT(DataSize::Infinity(), large);
}
+TEST(DataSizeTest, ConvertsToAndFromDouble) {
+ const int64_t kValue = 128;
+ const double kDoubleValue = static_cast<double>(kValue);
+
+ EXPECT_EQ(DataSize::bytes(kValue).bytes<double>(), kDoubleValue);
+ EXPECT_EQ(DataSize::bytes(kDoubleValue).bytes(), kValue);
+
+ const double kInfinity = std::numeric_limits<double>::infinity();
+ EXPECT_EQ(DataSize::Infinity().bytes<double>(), kInfinity);
+ EXPECT_TRUE(DataSize::bytes(kInfinity).IsInfinite());
+}
+
TEST(DataSizeTest, MathOperations) {
const int64_t kValueA = 450;
const int64_t kValueB = 267;
@@ -73,6 +80,7 @@
EXPECT_EQ((size_a * kFloatValue).bytes(), kValueA * kFloatValue);
EXPECT_EQ((size_a / 10).bytes(), kValueA / 10);
+ EXPECT_EQ(size_a / size_b, static_cast<double>(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 2491920..0f99e80 100644
--- a/api/units/time_delta.h
+++ b/api/units/time_delta.h
@@ -17,6 +17,7 @@
#include <string>
#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc {
namespace timedelta_impl {
@@ -41,33 +42,107 @@
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<std::is_integral<T>::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<int64_t>(seconds) * 1000000);
}
- static TimeDelta ms(int64_t milliseconds) {
- return TimeDelta::us(milliseconds * 1000);
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::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<int64_t>(milliseconds) * 1000);
}
- static TimeDelta us(int64_t microseconds) {
- // Infinities only allowed via use of explicit constants.
- RTC_DCHECK(microseconds > std::numeric_limits<int64_t>::min());
- RTC_DCHECK(microseconds < std::numeric_limits<int64_t>::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<int64_t>::min() / 1000);
- RTC_DCHECK(us() < std::numeric_limits<int64_t>::max() / 1000);
- return us() * 1000;
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::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<int64_t>(microseconds));
}
- double SecondsAsDouble() const;
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static TimeDelta seconds(T seconds) {
+ return TimeDelta::us(seconds * 1e6);
+ }
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static TimeDelta ms(T milliseconds) {
+ return TimeDelta::us(milliseconds * 1e3);
+ }
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static TimeDelta us(T microseconds) {
+ if (microseconds == std::numeric_limits<T>::infinity()) {
+ return PlusInfinity();
+ } else if (microseconds == -std::numeric_limits<T>::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<int64_t>(microseconds));
+ }
+ }
+
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type seconds() const {
+ return rtc::dchecked_cast<T>((us() + (us() >= 0 ? 500000 : -500000)) /
+ 1000000);
+ }
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type ms() const {
+ return rtc::dchecked_cast<T>((us() + (us() >= 0 ? 500 : -500)) / 1000);
+ }
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type us() const {
+ RTC_DCHECK(IsFinite());
+ return rtc::dchecked_cast<T>(microseconds_);
+ }
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type ns() const {
+ RTC_DCHECK_GE(us(), std::numeric_limits<T>::min() / 1000);
+ RTC_DCHECK_LE(us(), std::numeric_limits<T>::max() / 1000);
+ return rtc::dchecked_cast<T>(us() * 1000);
+ }
+
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type seconds()
+ const {
+ return us<T>() * 1e-6;
+ }
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type ms()
+ const {
+ return us<T>() * 1e-3;
+ }
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type us()
+ const {
+ if (IsPlusInfinity()) {
+ return std::numeric_limits<T>::infinity();
+ } else if (IsMinusInfinity()) {
+ return -std::numeric_limits<T>::infinity();
+ } else {
+ return microseconds_;
+ }
+ }
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type ns()
+ const {
+ return us<T>() * 1e3;
+ }
TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); }
bool IsZero() const { return microseconds_ == 0; }
@@ -96,7 +171,9 @@
microseconds_ += other.us();
return *this;
}
-
+ double operator/(const TimeDelta& other) const {
+ return us<double>() / other.us<double>();
+ }
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 493c6bf..94a27f2 100644
--- a/api/units/time_delta_unittest.cc
+++ b/api/units/time_delta_unittest.cc
@@ -80,6 +80,51 @@
EXPECT_LT(TimeDelta::MinusInfinity(), TimeDelta::Zero());
}
+TEST(TimeDeltaTest, CanBeInititializedFromLargeInt) {
+ const int kMaxInt = std::numeric_limits<int>::max();
+ EXPECT_EQ(TimeDelta::seconds(kMaxInt).us(),
+ static_cast<int64_t>(kMaxInt) * 1000000);
+ EXPECT_EQ(TimeDelta::ms(kMaxInt).us(), static_cast<int64_t>(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<double>(), kSecondsDouble);
+ EXPECT_EQ(TimeDelta::seconds(kSecondsDouble).us(), kMicros);
+
+ EXPECT_EQ(TimeDelta::us(kMicros).ms<double>(), kMillisDouble);
+ EXPECT_EQ(TimeDelta::ms(kMillisDouble).us(), kMicros);
+
+ EXPECT_EQ(TimeDelta::us(kMicros).us<double>(), kMicrosDouble);
+ EXPECT_EQ(TimeDelta::us(kMicrosDouble).us(), kMicros);
+
+ EXPECT_NEAR(TimeDelta::us(kMicros).ns<double>(), kNanosDouble, 1);
+
+ const double kPlusInfinity = std::numeric_limits<double>::infinity();
+ const double kMinusInfinity = -kPlusInfinity;
+
+ EXPECT_EQ(TimeDelta::PlusInfinity().seconds<double>(), kPlusInfinity);
+ EXPECT_EQ(TimeDelta::MinusInfinity().seconds<double>(), kMinusInfinity);
+ EXPECT_EQ(TimeDelta::PlusInfinity().ms<double>(), kPlusInfinity);
+ EXPECT_EQ(TimeDelta::MinusInfinity().ms<double>(), kMinusInfinity);
+ EXPECT_EQ(TimeDelta::PlusInfinity().us<double>(), kPlusInfinity);
+ EXPECT_EQ(TimeDelta::MinusInfinity().us<double>(), kMinusInfinity);
+ EXPECT_EQ(TimeDelta::PlusInfinity().ns<double>(), kPlusInfinity);
+ EXPECT_EQ(TimeDelta::MinusInfinity().ns<double>(), 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 @@
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<double>(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 7ae084c..4b2c44b 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<double>::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 40f3e92..a66e061 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 @@
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<std::is_integral<T>::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<int64_t>(seconds) * 1000000);
}
- double SecondsAsDouble() const;
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::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<int64_t>(milliseconds) * 1000);
+ }
+
+ template <
+ typename T,
+ typename std::enable_if<std::is_integral<T>::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<int64_t>(microseconds));
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static Timestamp seconds(T seconds) {
+ return Timestamp::us(seconds * 1e6);
+ }
+
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static Timestamp ms(T milliseconds) {
+ return Timestamp::us(milliseconds * 1e3);
+ }
+ template <typename T,
+ typename std::enable_if<std::is_floating_point<T>::value>::type* =
+ nullptr>
+ static Timestamp us(T microseconds) {
+ if (microseconds == std::numeric_limits<double>::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<int64_t>(microseconds));
+ }
+ }
+
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type seconds() const {
+ return rtc::dchecked_cast<T>((us() + 500000) / 1000000);
+ }
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type ms() const {
+ return rtc::dchecked_cast<T>((us() + 500) / 1000);
+ }
+ template <typename T = int64_t>
+ typename std::enable_if<std::is_integral<T>::value, T>::type us() const {
+ RTC_DCHECK(IsFinite());
+ return rtc::dchecked_cast<T>(microseconds_);
+ }
+
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type seconds()
+ const {
+ return us<T>() * 1e-6;
+ }
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type ms()
+ const {
+ return us<T>() * 1e-3;
+ }
+ template <typename T>
+ typename std::enable_if<std::is_floating_point<T>::value, T>::type us()
+ const {
+ if (IsInfinite()) {
+ return std::numeric_limits<T>::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 3b2770c..eecfe02 100644
--- a/api/units/timestamp_unittest.cc
+++ b/api/units/timestamp_unittest.cc
@@ -58,6 +58,39 @@
EXPECT_GT(Timestamp::ms(kLarge), Timestamp::ms(kSmall));
}
+TEST(TimestampTest, CanBeInititializedFromLargeInt) {
+ const int kMaxInt = std::numeric_limits<int>::max();
+ EXPECT_EQ(Timestamp::seconds(kMaxInt).us(),
+ static_cast<int64_t>(kMaxInt) * 1000000);
+ EXPECT_EQ(Timestamp::ms(kMaxInt).us(), static_cast<int64_t>(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<double>(), kSecondsDouble);
+ EXPECT_EQ(Timestamp::seconds(kSecondsDouble).us(), kMicros);
+
+ EXPECT_EQ(Timestamp::us(kMicros).ms<double>(), kMillisDouble);
+ EXPECT_EQ(Timestamp::ms(kMillisDouble).us(), kMicros);
+
+ EXPECT_EQ(Timestamp::us(kMicros).us<double>(), kMicrosDouble);
+ EXPECT_EQ(Timestamp::us(kMicrosDouble).us(), kMicros);
+
+ const double kPlusInfinity = std::numeric_limits<double>::infinity();
+
+ EXPECT_EQ(Timestamp::Infinity().seconds<double>(), kPlusInfinity);
+ EXPECT_EQ(Timestamp::Infinity().ms<double>(), kPlusInfinity);
+ EXPECT_EQ(Timestamp::Infinity().us<double>(), 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;