blob: ec364172aa18df3a84fccdcafa1ffaaf92cd7dfd [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>
Jonas Olsson941a07c2018-09-13 10:07:07 +020020#include <cstdlib>
Sebastian Jansson30bd4032018-04-13 13:56:17 +020021#include <limits>
22#include <string>
23
24#include "rtc_base/checks.h"
Sebastian Jansson942b3602018-05-30 15:47:44 +020025#include "rtc_base/numerics/safe_conversions.h"
Sebastian Jansson30bd4032018-04-13 13:56:17 +020026
27namespace webrtc {
28namespace timedelta_impl {
29constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
30constexpr int64_t kMinusInfinityVal = std::numeric_limits<int64_t>::min();
Sebastian Jansson30bd4032018-04-13 13:56:17 +020031} // namespace timedelta_impl
32
33// TimeDelta represents the difference between two timestamps. Commonly this can
34// be a duration. However since two Timestamps are not guaranteed to have the
35// same epoch (they might come from different computers, making exact
36// synchronisation infeasible), the duration covered by a TimeDelta can be
37// undefined. To simplify usage, it can be constructed and converted to
38// different units, specifically seconds (s), milliseconds (ms) and
39// microseconds (us).
40class TimeDelta {
41 public:
Sebastian Jansson3b69b192018-05-07 13:51:51 +020042 TimeDelta() = delete;
Sebastian Jansson8e064192018-08-07 12:34:33 +020043 static constexpr TimeDelta Zero() { return TimeDelta(0); }
44 static constexpr TimeDelta PlusInfinity() {
Sebastian Jansson30bd4032018-04-13 13:56:17 +020045 return TimeDelta(timedelta_impl::kPlusInfinityVal);
46 }
Sebastian Jansson8e064192018-08-07 12:34:33 +020047 static constexpr TimeDelta MinusInfinity() {
Sebastian Jansson30bd4032018-04-13 13:56:17 +020048 return TimeDelta(timedelta_impl::kMinusInfinityVal);
49 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +020050 template <int64_t seconds>
51 static constexpr TimeDelta Seconds() {
52 static_assert(seconds > timedelta_impl::kMinusInfinityVal / 1000000, "");
53 static_assert(seconds < timedelta_impl::kPlusInfinityVal / 1000000, "");
54 return TimeDelta(seconds * 1000000);
55 }
56 template <int64_t ms>
57 static constexpr TimeDelta Millis() {
58 static_assert(ms > timedelta_impl::kMinusInfinityVal / 1000, "");
59 static_assert(ms < timedelta_impl::kPlusInfinityVal / 1000, "");
60 return TimeDelta(ms * 1000);
61 }
62 template <int64_t us>
63 static constexpr TimeDelta Micros() {
64 static_assert(us > timedelta_impl::kMinusInfinityVal, "");
65 static_assert(us < timedelta_impl::kPlusInfinityVal, "");
66 return TimeDelta(us);
67 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020068
69 template <
70 typename T,
71 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
72 static TimeDelta seconds(T seconds) {
73 RTC_DCHECK_GT(seconds, timedelta_impl::kMinusInfinityVal / 1000000);
74 RTC_DCHECK_LT(seconds, timedelta_impl::kPlusInfinityVal / 1000000);
75 return TimeDelta(rtc::dchecked_cast<int64_t>(seconds) * 1000000);
Sebastian Jansson30bd4032018-04-13 13:56:17 +020076 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020077 template <
78 typename T,
79 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
80 static TimeDelta ms(T milliseconds) {
81 RTC_DCHECK_GT(milliseconds, timedelta_impl::kMinusInfinityVal / 1000);
82 RTC_DCHECK_LT(milliseconds, timedelta_impl::kPlusInfinityVal / 1000);
83 return TimeDelta(rtc::dchecked_cast<int64_t>(milliseconds) * 1000);
Sebastian Jansson30bd4032018-04-13 13:56:17 +020084 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020085 template <
86 typename T,
87 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
88 static TimeDelta us(T microseconds) {
89 RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
90 RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
91 return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
Sebastian Jansson5f83cf02018-05-08 14:52:22 +020092 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +020093
Sebastian Jansson942b3602018-05-30 15:47:44 +020094 template <typename T,
95 typename std::enable_if<std::is_floating_point<T>::value>::type* =
96 nullptr>
97 static TimeDelta seconds(T seconds) {
98 return TimeDelta::us(seconds * 1e6);
99 }
100 template <typename T,
101 typename std::enable_if<std::is_floating_point<T>::value>::type* =
102 nullptr>
103 static TimeDelta ms(T milliseconds) {
104 return TimeDelta::us(milliseconds * 1e3);
105 }
106 template <typename T,
107 typename std::enable_if<std::is_floating_point<T>::value>::type* =
108 nullptr>
109 static TimeDelta us(T microseconds) {
110 if (microseconds == std::numeric_limits<T>::infinity()) {
111 return PlusInfinity();
112 } else if (microseconds == -std::numeric_limits<T>::infinity()) {
113 return MinusInfinity();
114 } else {
115 RTC_DCHECK(!std::isnan(microseconds));
116 RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
117 RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
118 return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
119 }
120 }
121
122 template <typename T = int64_t>
123 typename std::enable_if<std::is_integral<T>::value, T>::type seconds() const {
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200124 RTC_DCHECK(IsFinite());
125 return rtc::dchecked_cast<T>(UnsafeSeconds());
Sebastian Jansson942b3602018-05-30 15:47:44 +0200126 }
127 template <typename T = int64_t>
128 typename std::enable_if<std::is_integral<T>::value, T>::type ms() const {
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200129 RTC_DCHECK(IsFinite());
130 return rtc::dchecked_cast<T>(UnsafeMillis());
Sebastian Jansson942b3602018-05-30 15:47:44 +0200131 }
132 template <typename T = int64_t>
133 typename std::enable_if<std::is_integral<T>::value, T>::type us() const {
134 RTC_DCHECK(IsFinite());
135 return rtc::dchecked_cast<T>(microseconds_);
136 }
137 template <typename T = int64_t>
138 typename std::enable_if<std::is_integral<T>::value, T>::type ns() const {
139 RTC_DCHECK_GE(us(), std::numeric_limits<T>::min() / 1000);
140 RTC_DCHECK_LE(us(), std::numeric_limits<T>::max() / 1000);
141 return rtc::dchecked_cast<T>(us() * 1000);
142 }
143
144 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200145 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
146 seconds() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200147 return us<T>() * 1e-6;
148 }
149 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200150 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
151 ms() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200152 return us<T>() * 1e-3;
153 }
154 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200155 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
156 us() const {
157 return IsPlusInfinity()
158 ? std::numeric_limits<T>::infinity()
159 : IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
160 : microseconds_;
Sebastian Jansson942b3602018-05-30 15:47:44 +0200161 }
162 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200163 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
164 ns() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200165 return us<T>() * 1e3;
166 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200167
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200168 constexpr int64_t seconds_or(int64_t fallback_value) const {
169 return IsFinite() ? UnsafeSeconds() : fallback_value;
170 }
171 constexpr int64_t ms_or(int64_t fallback_value) const {
172 return IsFinite() ? UnsafeMillis() : fallback_value;
173 }
174 constexpr int64_t us_or(int64_t fallback_value) const {
175 return IsFinite() ? microseconds_ : fallback_value;
176 }
177
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200178 TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200179 constexpr bool IsZero() const { return microseconds_ == 0; }
180 constexpr bool IsFinite() const { return !IsInfinite(); }
181 constexpr bool IsInfinite() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200182 return microseconds_ == timedelta_impl::kPlusInfinityVal ||
183 microseconds_ == timedelta_impl::kMinusInfinityVal;
184 }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200185 constexpr bool IsPlusInfinity() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200186 return microseconds_ == timedelta_impl::kPlusInfinityVal;
187 }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200188 constexpr bool IsMinusInfinity() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200189 return microseconds_ == timedelta_impl::kMinusInfinityVal;
190 }
191 TimeDelta operator+(const TimeDelta& other) const {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200192 if (IsPlusInfinity() || other.IsPlusInfinity()) {
193 RTC_DCHECK(!IsMinusInfinity());
194 RTC_DCHECK(!other.IsMinusInfinity());
195 return PlusInfinity();
196 } else if (IsMinusInfinity() || other.IsMinusInfinity()) {
197 RTC_DCHECK(!IsPlusInfinity());
198 RTC_DCHECK(!other.IsPlusInfinity());
199 return MinusInfinity();
200 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200201 return TimeDelta::us(us() + other.us());
202 }
203 TimeDelta operator-(const TimeDelta& other) const {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200204 if (IsPlusInfinity() || other.IsMinusInfinity()) {
205 RTC_DCHECK(!IsMinusInfinity());
206 RTC_DCHECK(!other.IsPlusInfinity());
207 return PlusInfinity();
208 } else if (IsMinusInfinity() || other.IsPlusInfinity()) {
209 RTC_DCHECK(!IsPlusInfinity());
210 RTC_DCHECK(!other.IsMinusInfinity());
211 return MinusInfinity();
212 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200213 return TimeDelta::us(us() - other.us());
214 }
215 TimeDelta& operator-=(const TimeDelta& other) {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200216 *this = *this - other;
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200217 return *this;
218 }
219 TimeDelta& operator+=(const TimeDelta& other) {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200220 *this = *this + other;
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200221 return *this;
222 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200223 constexpr double operator/(const TimeDelta& other) const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200224 return us<double>() / other.us<double>();
225 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200226 constexpr bool operator==(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200227 return microseconds_ == other.microseconds_;
228 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200229 constexpr bool operator!=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200230 return microseconds_ != other.microseconds_;
231 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200232 constexpr bool operator<=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200233 return microseconds_ <= other.microseconds_;
234 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200235 constexpr bool operator>=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200236 return microseconds_ >= other.microseconds_;
237 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200238 constexpr bool operator>(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200239 return microseconds_ > other.microseconds_;
240 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200241 constexpr bool operator<(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200242 return microseconds_ < other.microseconds_;
243 }
244
245 private:
Sebastian Jansson8e064192018-08-07 12:34:33 +0200246 explicit constexpr TimeDelta(int64_t us) : microseconds_(us) {}
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200247 constexpr int64_t UnsafeSeconds() const {
248 return (microseconds_ + (microseconds_ >= 0 ? 500000 : -500000)) / 1000000;
249 }
250 constexpr int64_t UnsafeMillis() const {
251 return (microseconds_ + (microseconds_ >= 0 ? 500 : -500)) / 1000;
252 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200253 int64_t microseconds_;
254};
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200255
256inline TimeDelta operator*(const TimeDelta& delta, const double& scalar) {
257 return TimeDelta::us(std::round(delta.us() * scalar));
258}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200259inline TimeDelta operator*(const double& scalar, const TimeDelta& delta) {
260 return delta * scalar;
261}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200262inline TimeDelta operator*(const TimeDelta& delta, const int64_t& scalar) {
263 return TimeDelta::us(delta.us() * scalar);
264}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200265inline TimeDelta operator*(const int64_t& scalar, const TimeDelta& delta) {
266 return delta * scalar;
267}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200268inline TimeDelta operator*(const TimeDelta& delta, const int32_t& scalar) {
269 return TimeDelta::us(delta.us() * scalar);
270}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200271inline TimeDelta operator*(const int32_t& scalar, const TimeDelta& delta) {
272 return delta * scalar;
273}
274
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200275inline TimeDelta operator/(const TimeDelta& delta, const int64_t& scalar) {
276 return TimeDelta::us(delta.us() / scalar);
277}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200278std::string ToString(const TimeDelta& value);
Sebastian Jansson2afd2812018-08-23 14:44:05 +0200279
280#ifdef UNIT_TEST
281inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
282 std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
283 TimeDelta value) {
284 return stream << ToString(value);
285}
286#endif // UNIT_TEST
287
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200288} // namespace webrtc
289
Sebastian Jansson6fae6ec2018-05-08 10:43:18 +0200290#endif // API_UNITS_TIME_DELTA_H_