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}
This commit is contained in:
Sebastian Jansson
2018-05-30 15:47:44 +02:00
committed by Commit Bot
parent f859e55d9b
commit 942b360d82
9 changed files with 424 additions and 87 deletions

View File

@ -16,6 +16,7 @@
#include <string> #include <string>
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "api/units/data_size.h" #include "api/units/data_size.h"
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
@ -43,28 +44,78 @@ class DataRate {
static DataRate Infinity() { static DataRate Infinity() {
return DataRate(data_rate_impl::kPlusInfinityVal); return DataRate(data_rate_impl::kPlusInfinityVal);
} }
static DataRate bits_per_second(int64_t bits_per_sec) {
RTC_DCHECK_GE(bits_per_sec, 0); template <
return DataRate(bits_per_sec); 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) { template <
return DataRate::bits_per_second(bits_per_sec); 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()); RTC_DCHECK(IsFinite());
return bits_per_sec_; return rtc::dchecked_cast<T>(bits_per_sec_);
} }
int64_t bps() const { return bits_per_second(); } template <typename T = int64_t>
int64_t kbps() const { return (bps() + 500) / 1000; } 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 IsZero() const { return bits_per_sec_ == 0; }
bool IsInfinite() const { bool IsInfinite() const {
return bits_per_sec_ == data_rate_impl::kPlusInfinityVal; return bits_per_sec_ == data_rate_impl::kPlusInfinityVal;
} }
bool IsFinite() const { return !IsInfinite(); } bool IsFinite() const { return !IsInfinite(); }
double operator/(const DataRate& other) const {
return bps<double>() / other.bps<double>();
}
bool operator==(const DataRate& other) const { bool operator==(const DataRate& other) const {
return bits_per_sec_ == other.bits_per_sec_; return bits_per_sec_ == other.bits_per_sec_;
} }
@ -92,34 +143,32 @@ class DataRate {
}; };
inline DataRate operator*(const DataRate& rate, const double& scalar) { 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) { inline DataRate operator*(const double& scalar, const DataRate& rate) {
return rate * scalar; return rate * scalar;
} }
inline DataRate operator*(const DataRate& rate, const int64_t& 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) { inline DataRate operator*(const int64_t& scalar, const DataRate& rate) {
return rate * scalar; return rate * scalar;
} }
inline DataRate operator*(const DataRate& rate, const int32_t& 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) { inline DataRate operator*(const int32_t& scalar, const DataRate& rate) {
return rate * scalar; return rate * scalar;
} }
inline DataRate operator/(const DataSize& size, const TimeDelta& duration) { inline DataRate operator/(const DataSize& size, const TimeDelta& duration) {
return DataRate::bits_per_second(data_rate_impl::Microbits(size) / return DataRate::bps(data_rate_impl::Microbits(size) / duration.us());
duration.us());
} }
inline TimeDelta operator/(const DataSize& size, const DataRate& rate) { inline TimeDelta operator/(const DataSize& size, const DataRate& rate) {
return TimeDelta::us(data_rate_impl::Microbits(size) / return TimeDelta::us(data_rate_impl::Microbits(size) / rate.bps());
rate.bits_per_second());
} }
inline DataSize operator*(const DataRate& rate, const TimeDelta& duration) { 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); return DataSize::bytes((microbits + 4000000) / 8000000);
} }
inline DataSize operator*(const TimeDelta& duration, const DataRate& rate) { inline DataSize operator*(const TimeDelta& duration, const DataRate& rate) {

View File

@ -15,7 +15,6 @@ namespace webrtc {
namespace test { namespace test {
TEST(DataRateTest, GetBackSameValues) { TEST(DataRateTest, GetBackSameValues) {
const int64_t kValue = 123 * 8; 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::bps(kValue).bps(), kValue);
EXPECT_EQ(DataRate::kbps(kValue).kbps(), kValue); EXPECT_EQ(DataRate::kbps(kValue).kbps(), kValue);
} }
@ -28,22 +27,22 @@ TEST(DataRateTest, GetDifferentPrefix) {
TEST(DataRateTest, IdentityChecks) { TEST(DataRateTest, IdentityChecks) {
const int64_t kValue = 3000; const int64_t kValue = 3000;
EXPECT_TRUE(DataRate::Zero().IsZero()); 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_TRUE(DataRate::Infinity().IsInfinite());
EXPECT_FALSE(DataRate::Zero().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_FALSE(DataRate::Infinity().IsFinite());
EXPECT_TRUE(DataRate::bits_per_second(kValue).IsFinite()); EXPECT_TRUE(DataRate::bps(kValue).IsFinite());
EXPECT_TRUE(DataRate::Zero().IsFinite()); EXPECT_TRUE(DataRate::Zero().IsFinite());
} }
TEST(DataRateTest, ComparisonOperators) { TEST(DataRateTest, ComparisonOperators) {
const int64_t kSmall = 450; const int64_t kSmall = 450;
const int64_t kLarge = 451; const int64_t kLarge = 451;
const DataRate small = DataRate::bits_per_second(kSmall); const DataRate small = DataRate::bps(kSmall);
const DataRate large = DataRate::bits_per_second(kLarge); const DataRate large = DataRate::bps(kLarge);
EXPECT_EQ(DataRate::Zero(), DataRate::bps(0)); EXPECT_EQ(DataRate::Zero(), DataRate::bps(0));
EXPECT_EQ(DataRate::Infinity(), DataRate::Infinity()); EXPECT_EQ(DataRate::Infinity(), DataRate::Infinity());
@ -59,15 +58,36 @@ TEST(DataRateTest, ComparisonOperators) {
EXPECT_GT(DataRate::Infinity(), large); 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) { TEST(DataRateTest, MathOperations) {
const int64_t kValueA = 450; const int64_t kValueA = 450;
const int64_t kValueB = 267; 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 int32_t kInt32Value = 123;
const double kFloatValue = 123.0; const double kFloatValue = 123.0;
EXPECT_EQ((size_a * kValueB).bits_per_second(), kValueA * kValueB); EXPECT_EQ((rate_a * kValueB).bps(), kValueA * kValueB);
EXPECT_EQ((size_a * kInt32Value).bits_per_second(), kValueA * kInt32Value); EXPECT_EQ((rate_a * kInt32Value).bps(), kValueA * kInt32Value);
EXPECT_EQ((size_a * kFloatValue).bits_per_second(), kValueA * kFloatValue); EXPECT_EQ((rate_a * kFloatValue).bps(), kValueA * kFloatValue);
EXPECT_EQ(rate_a / rate_b, static_cast<double>(kValueA) / kValueB);
} }
TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) { TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) {
@ -75,11 +95,11 @@ TEST(UnitConversionTest, DataRateAndDataSizeAndTimeDelta) {
const int64_t kBitsPerSecond = 440; const int64_t kBitsPerSecond = 440;
const int64_t kBytes = 44000; const int64_t kBytes = 44000;
const TimeDelta delta_a = TimeDelta::seconds(kSeconds); 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); const DataSize size_c = DataSize::bytes(kBytes);
EXPECT_EQ((delta_a * rate_b).bytes(), kSeconds * kBitsPerSecond / 8); EXPECT_EQ((delta_a * rate_b).bytes(), kSeconds * kBitsPerSecond / 8);
EXPECT_EQ((rate_b * delta_a).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); EXPECT_EQ((size_c / rate_b).seconds(), kBytes * 8 / kBitsPerSecond);
} }

View File

@ -15,8 +15,10 @@
#include <cmath> #include <cmath>
#include <limits> #include <limits>
#include <string> #include <string>
#include <type_traits>
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc { namespace webrtc {
namespace data_size_impl { namespace data_size_impl {
@ -31,15 +33,46 @@ class DataSize {
static DataSize Infinity() { static DataSize Infinity() {
return DataSize(data_size_impl::kPlusInfinityVal); 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); 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()); 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 IsZero() const { return bytes_ == 0; }
bool IsInfinite() const { return bytes_ == data_size_impl::kPlusInfinityVal; } bool IsInfinite() const { return bytes_ == data_size_impl::kPlusInfinityVal; }
bool IsFinite() const { return !IsInfinite(); } bool IsFinite() const { return !IsInfinite(); }
@ -57,6 +90,9 @@ class DataSize {
bytes_ += other.bytes(); bytes_ += other.bytes();
return *this; return *this;
} }
double operator/(const DataSize& other) const {
return bytes<double>() / other.bytes<double>();
}
bool operator==(const DataSize& other) const { bool operator==(const DataSize& other) const {
return bytes_ == other.bytes_; return bytes_ == other.bytes_;
} }
@ -76,6 +112,7 @@ class DataSize {
explicit DataSize(int64_t bytes) : bytes_(bytes) {} explicit DataSize(int64_t bytes) : bytes_(bytes) {}
int64_t bytes_; int64_t bytes_;
}; };
inline DataSize operator*(const DataSize& size, const double& scalar) { inline DataSize operator*(const DataSize& size, const double& scalar) {
return DataSize::bytes(std::round(size.bytes() * scalar)); return DataSize::bytes(std::round(size.bytes() * scalar));
} }

View File

@ -19,11 +19,6 @@ TEST(DataSizeTest, GetBackSameValues) {
EXPECT_EQ(DataSize::bytes(kValue).bytes(), kValue); 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) { TEST(DataSizeTest, IdentityChecks) {
const int64_t kValue = 3000; const int64_t kValue = 3000;
EXPECT_TRUE(DataSize::Zero().IsZero()); EXPECT_TRUE(DataSize::Zero().IsZero());
@ -58,6 +53,18 @@ TEST(DataSizeTest, ComparisonOperators) {
EXPECT_GT(DataSize::Infinity(), large); 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) { TEST(DataSizeTest, MathOperations) {
const int64_t kValueA = 450; const int64_t kValueA = 450;
const int64_t kValueB = 267; const int64_t kValueB = 267;
@ -73,6 +80,7 @@ TEST(DataSizeTest, MathOperations) {
EXPECT_EQ((size_a * kFloatValue).bytes(), kValueA * kFloatValue); EXPECT_EQ((size_a * kFloatValue).bytes(), kValueA * kFloatValue);
EXPECT_EQ((size_a / 10).bytes(), kValueA / 10); 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); DataSize mutable_size = DataSize::bytes(kValueA);
mutable_size += size_b; mutable_size += size_b;

View File

@ -17,6 +17,7 @@
#include <string> #include <string>
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc { namespace webrtc {
namespace timedelta_impl { namespace timedelta_impl {
@ -41,33 +42,107 @@ class TimeDelta {
static TimeDelta MinusInfinity() { static TimeDelta MinusInfinity() {
return TimeDelta(timedelta_impl::kMinusInfinityVal); 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) { template <
return TimeDelta::us(milliseconds * 1000); 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) { template <
// Infinities only allowed via use of explicit constants. typename T,
RTC_DCHECK(microseconds > std::numeric_limits<int64_t>::min()); typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
RTC_DCHECK(microseconds < std::numeric_limits<int64_t>::max()); static TimeDelta us(T microseconds) {
return TimeDelta(microseconds); RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
} RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
int64_t seconds() const { return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
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;
} }
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())); } TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); }
bool IsZero() const { return microseconds_ == 0; } bool IsZero() const { return microseconds_ == 0; }
@ -96,7 +171,9 @@ class TimeDelta {
microseconds_ += other.us(); microseconds_ += other.us();
return *this; return *this;
} }
double operator/(const TimeDelta& other) const {
return us<double>() / other.us<double>();
}
bool operator==(const TimeDelta& other) const { bool operator==(const TimeDelta& other) const {
return microseconds_ == other.microseconds_; return microseconds_ == other.microseconds_;
} }

View File

@ -80,6 +80,51 @@ TEST(TimeDeltaTest, ComparisonOperators) {
EXPECT_LT(TimeDelta::MinusInfinity(), TimeDelta::Zero()); 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) { TEST(TimeDeltaTest, MathOperations) {
const int64_t kValueA = 267; const int64_t kValueA = 267;
const int64_t kValueB = 450; 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) * kInt32Value).us(), kValueA * kInt32Value);
EXPECT_EQ((TimeDelta::us(kValueA) * kFloatValue).us(), kValueA * kFloatValue); 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);
EXPECT_EQ(TimeDelta::us(kValueA).Abs().us(), kValueA); EXPECT_EQ(TimeDelta::us(kValueA).Abs().us(), kValueA);
} }

View File

@ -13,14 +13,6 @@
#include "rtc_base/strings/string_builder.h" #include "rtc_base/strings/string_builder.h"
namespace webrtc { 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) { std::string ToString(const Timestamp& value) {
char buf[64]; char buf[64];
rtc::SimpleStringBuilder sb(buf); rtc::SimpleStringBuilder sb(buf);

View File

@ -17,6 +17,7 @@
#include "api/units/time_delta.h" #include "api/units/time_delta.h"
#include "rtc_base/checks.h" #include "rtc_base/checks.h"
#include "rtc_base/numerics/safe_conversions.h"
namespace webrtc { namespace webrtc {
namespace timestamp_impl { namespace timestamp_impl {
@ -34,22 +35,94 @@ class Timestamp {
static Timestamp Infinity() { static Timestamp Infinity() {
return Timestamp(timestamp_impl::kPlusInfinityVal); return Timestamp(timestamp_impl::kPlusInfinityVal);
} }
static Timestamp seconds(int64_t seconds) {
return Timestamp::us(seconds * 1000000); template <
} typename T,
static Timestamp ms(int64_t millis) { return Timestamp::us(millis * 1000); } typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
static Timestamp us(int64_t micros) { static Timestamp seconds(T seconds) {
RTC_DCHECK_GE(micros, 0); RTC_DCHECK_GE(seconds, 0);
return Timestamp(micros); RTC_DCHECK_LT(seconds, timestamp_impl::kPlusInfinityVal / 1000000);
} return Timestamp(rtc::dchecked_cast<int64_t>(seconds) * 1000000);
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_;
} }
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 { bool IsInfinite() const {
return microseconds_ == timestamp_impl::kPlusInfinityVal; return microseconds_ == timestamp_impl::kPlusInfinityVal;

View File

@ -58,6 +58,39 @@ TEST(TimestampTest, ComparisonOperators) {
EXPECT_GT(Timestamp::ms(kLarge), Timestamp::ms(kSmall)); 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) { TEST(UnitConversionTest, TimestampAndTimeDeltaMath) {
const int64_t kValueA = 267; const int64_t kValueA = 267;
const int64_t kValueB = 450; const int64_t kValueB = 450;