blob: b82047285f8dcf3e0cac867ec3d0856366b43da3 [file] [log] [blame]
Sebastian Jansson30bd4032018-04-13 13:56:17 +02001/*
2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Sebastian Jansson6fae6ec2018-05-08 10:43:18 +020011#ifndef API_UNITS_TIME_DELTA_H_
12#define API_UNITS_TIME_DELTA_H_
Sebastian Jansson30bd4032018-04-13 13:56:17 +020013
Sebastian Jansson2afd2812018-08-23 14:44:05 +020014#ifdef UNIT_TEST
15#include <ostream> // no-presubmit-check TODO(webrtc:8982)
16#endif // UNIT_TEST
17
Sebastian Jansson30bd4032018-04-13 13:56:17 +020018#include <stdint.h>
19#include <cmath>
20#include <limits>
21#include <string>
22
23#include "rtc_base/checks.h"
Sebastian Jansson942b3602018-05-30 15:47:44 +020024#include "rtc_base/numerics/safe_conversions.h"
Sebastian Jansson30bd4032018-04-13 13:56:17 +020025
26namespace webrtc {
27namespace timedelta_impl {
28constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
29constexpr int64_t kMinusInfinityVal = std::numeric_limits<int64_t>::min();
Sebastian Jansson30bd4032018-04-13 13:56:17 +020030} // namespace timedelta_impl
31
32// TimeDelta represents the difference between two timestamps. Commonly this can
33// be a duration. However since two Timestamps are not guaranteed to have the
34// same epoch (they might come from different computers, making exact
35// synchronisation infeasible), the duration covered by a TimeDelta can be
36// undefined. To simplify usage, it can be constructed and converted to
37// different units, specifically seconds (s), milliseconds (ms) and
38// microseconds (us).
39class TimeDelta {
40 public:
Sebastian Jansson3b69b192018-05-07 13:51:51 +020041 TimeDelta() = delete;
Sebastian Jansson8e064192018-08-07 12:34:33 +020042 static constexpr TimeDelta Zero() { return TimeDelta(0); }
43 static constexpr TimeDelta PlusInfinity() {
Sebastian Jansson30bd4032018-04-13 13:56:17 +020044 return TimeDelta(timedelta_impl::kPlusInfinityVal);
45 }
Sebastian Jansson8e064192018-08-07 12:34:33 +020046 static constexpr TimeDelta MinusInfinity() {
Sebastian Jansson30bd4032018-04-13 13:56:17 +020047 return TimeDelta(timedelta_impl::kMinusInfinityVal);
48 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +020049 template <int64_t seconds>
50 static constexpr TimeDelta Seconds() {
51 static_assert(seconds > timedelta_impl::kMinusInfinityVal / 1000000, "");
52 static_assert(seconds < timedelta_impl::kPlusInfinityVal / 1000000, "");
53 return TimeDelta(seconds * 1000000);
54 }
55 template <int64_t ms>
56 static constexpr TimeDelta Millis() {
57 static_assert(ms > timedelta_impl::kMinusInfinityVal / 1000, "");
58 static_assert(ms < timedelta_impl::kPlusInfinityVal / 1000, "");
59 return TimeDelta(ms * 1000);
60 }
61 template <int64_t us>
62 static constexpr TimeDelta Micros() {
63 static_assert(us > timedelta_impl::kMinusInfinityVal, "");
64 static_assert(us < timedelta_impl::kPlusInfinityVal, "");
65 return TimeDelta(us);
66 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020067
68 template <
69 typename T,
70 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
71 static TimeDelta seconds(T seconds) {
72 RTC_DCHECK_GT(seconds, timedelta_impl::kMinusInfinityVal / 1000000);
73 RTC_DCHECK_LT(seconds, timedelta_impl::kPlusInfinityVal / 1000000);
74 return TimeDelta(rtc::dchecked_cast<int64_t>(seconds) * 1000000);
Sebastian Jansson30bd4032018-04-13 13:56:17 +020075 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020076 template <
77 typename T,
78 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
79 static TimeDelta ms(T milliseconds) {
80 RTC_DCHECK_GT(milliseconds, timedelta_impl::kMinusInfinityVal / 1000);
81 RTC_DCHECK_LT(milliseconds, timedelta_impl::kPlusInfinityVal / 1000);
82 return TimeDelta(rtc::dchecked_cast<int64_t>(milliseconds) * 1000);
Sebastian Jansson30bd4032018-04-13 13:56:17 +020083 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020084 template <
85 typename T,
86 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
87 static TimeDelta us(T microseconds) {
88 RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
89 RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
90 return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
Sebastian Jansson5f83cf02018-05-08 14:52:22 +020091 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +020092
Sebastian Jansson942b3602018-05-30 15:47:44 +020093 template <typename T,
94 typename std::enable_if<std::is_floating_point<T>::value>::type* =
95 nullptr>
96 static TimeDelta seconds(T seconds) {
97 return TimeDelta::us(seconds * 1e6);
98 }
99 template <typename T,
100 typename std::enable_if<std::is_floating_point<T>::value>::type* =
101 nullptr>
102 static TimeDelta ms(T milliseconds) {
103 return TimeDelta::us(milliseconds * 1e3);
104 }
105 template <typename T,
106 typename std::enable_if<std::is_floating_point<T>::value>::type* =
107 nullptr>
108 static TimeDelta us(T microseconds) {
109 if (microseconds == std::numeric_limits<T>::infinity()) {
110 return PlusInfinity();
111 } else if (microseconds == -std::numeric_limits<T>::infinity()) {
112 return MinusInfinity();
113 } else {
114 RTC_DCHECK(!std::isnan(microseconds));
115 RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
116 RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
117 return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
118 }
119 }
120
121 template <typename T = int64_t>
122 typename std::enable_if<std::is_integral<T>::value, T>::type seconds() const {
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200123 RTC_DCHECK(IsFinite());
124 return rtc::dchecked_cast<T>(UnsafeSeconds());
Sebastian Jansson942b3602018-05-30 15:47:44 +0200125 }
126 template <typename T = int64_t>
127 typename std::enable_if<std::is_integral<T>::value, T>::type ms() const {
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200128 RTC_DCHECK(IsFinite());
129 return rtc::dchecked_cast<T>(UnsafeMillis());
Sebastian Jansson942b3602018-05-30 15:47:44 +0200130 }
131 template <typename T = int64_t>
132 typename std::enable_if<std::is_integral<T>::value, T>::type us() const {
133 RTC_DCHECK(IsFinite());
134 return rtc::dchecked_cast<T>(microseconds_);
135 }
136 template <typename T = int64_t>
137 typename std::enable_if<std::is_integral<T>::value, T>::type ns() const {
138 RTC_DCHECK_GE(us(), std::numeric_limits<T>::min() / 1000);
139 RTC_DCHECK_LE(us(), std::numeric_limits<T>::max() / 1000);
140 return rtc::dchecked_cast<T>(us() * 1000);
141 }
142
143 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200144 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
145 seconds() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200146 return us<T>() * 1e-6;
147 }
148 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200149 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
150 ms() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200151 return us<T>() * 1e-3;
152 }
153 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200154 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
155 us() const {
156 return IsPlusInfinity()
157 ? std::numeric_limits<T>::infinity()
158 : IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
159 : microseconds_;
Sebastian Jansson942b3602018-05-30 15:47:44 +0200160 }
161 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200162 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
163 ns() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200164 return us<T>() * 1e3;
165 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200166
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200167 constexpr int64_t seconds_or(int64_t fallback_value) const {
168 return IsFinite() ? UnsafeSeconds() : fallback_value;
169 }
170 constexpr int64_t ms_or(int64_t fallback_value) const {
171 return IsFinite() ? UnsafeMillis() : fallback_value;
172 }
173 constexpr int64_t us_or(int64_t fallback_value) const {
174 return IsFinite() ? microseconds_ : fallback_value;
175 }
176
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200177 TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200178 constexpr bool IsZero() const { return microseconds_ == 0; }
179 constexpr bool IsFinite() const { return !IsInfinite(); }
180 constexpr bool IsInfinite() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200181 return microseconds_ == timedelta_impl::kPlusInfinityVal ||
182 microseconds_ == timedelta_impl::kMinusInfinityVal;
183 }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200184 constexpr bool IsPlusInfinity() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200185 return microseconds_ == timedelta_impl::kPlusInfinityVal;
186 }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200187 constexpr bool IsMinusInfinity() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200188 return microseconds_ == timedelta_impl::kMinusInfinityVal;
189 }
190 TimeDelta operator+(const TimeDelta& other) const {
191 return TimeDelta::us(us() + other.us());
192 }
193 TimeDelta operator-(const TimeDelta& other) const {
194 return TimeDelta::us(us() - other.us());
195 }
196 TimeDelta& operator-=(const TimeDelta& other) {
197 microseconds_ -= other.us();
198 return *this;
199 }
200 TimeDelta& operator+=(const TimeDelta& other) {
201 microseconds_ += other.us();
202 return *this;
203 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200204 constexpr double operator/(const TimeDelta& other) const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200205 return us<double>() / other.us<double>();
206 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200207 constexpr bool operator==(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200208 return microseconds_ == other.microseconds_;
209 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200210 constexpr bool operator!=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200211 return microseconds_ != other.microseconds_;
212 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200213 constexpr bool operator<=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200214 return microseconds_ <= other.microseconds_;
215 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200216 constexpr bool operator>=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200217 return microseconds_ >= other.microseconds_;
218 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200219 constexpr bool operator>(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200220 return microseconds_ > other.microseconds_;
221 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200222 constexpr bool operator<(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200223 return microseconds_ < other.microseconds_;
224 }
225
226 private:
Sebastian Jansson8e064192018-08-07 12:34:33 +0200227 explicit constexpr TimeDelta(int64_t us) : microseconds_(us) {}
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200228 constexpr int64_t UnsafeSeconds() const {
229 return (microseconds_ + (microseconds_ >= 0 ? 500000 : -500000)) / 1000000;
230 }
231 constexpr int64_t UnsafeMillis() const {
232 return (microseconds_ + (microseconds_ >= 0 ? 500 : -500)) / 1000;
233 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200234 int64_t microseconds_;
235};
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200236
237inline TimeDelta operator*(const TimeDelta& delta, const double& scalar) {
238 return TimeDelta::us(std::round(delta.us() * scalar));
239}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200240inline TimeDelta operator*(const double& scalar, const TimeDelta& delta) {
241 return delta * scalar;
242}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200243inline TimeDelta operator*(const TimeDelta& delta, const int64_t& scalar) {
244 return TimeDelta::us(delta.us() * scalar);
245}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200246inline TimeDelta operator*(const int64_t& scalar, const TimeDelta& delta) {
247 return delta * scalar;
248}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200249inline TimeDelta operator*(const TimeDelta& delta, const int32_t& scalar) {
250 return TimeDelta::us(delta.us() * scalar);
251}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200252inline TimeDelta operator*(const int32_t& scalar, const TimeDelta& delta) {
253 return delta * scalar;
254}
255
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200256inline TimeDelta operator/(const TimeDelta& delta, const int64_t& scalar) {
257 return TimeDelta::us(delta.us() / scalar);
258}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200259std::string ToString(const TimeDelta& value);
Sebastian Jansson2afd2812018-08-23 14:44:05 +0200260
261#ifdef UNIT_TEST
262inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
263 std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
264 TimeDelta value) {
265 return stream << ToString(value);
266}
267#endif // UNIT_TEST
268
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200269} // namespace webrtc
270
Sebastian Jansson6fae6ec2018-05-08 10:43:18 +0200271#endif // API_UNITS_TIME_DELTA_H_