blob: 6e69333fca617185f1b6817711d0e918a3e45a52 [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 {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200191 if (IsPlusInfinity() || other.IsPlusInfinity()) {
192 RTC_DCHECK(!IsMinusInfinity());
193 RTC_DCHECK(!other.IsMinusInfinity());
194 return PlusInfinity();
195 } else if (IsMinusInfinity() || other.IsMinusInfinity()) {
196 RTC_DCHECK(!IsPlusInfinity());
197 RTC_DCHECK(!other.IsPlusInfinity());
198 return MinusInfinity();
199 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200200 return TimeDelta::us(us() + other.us());
201 }
202 TimeDelta operator-(const TimeDelta& other) const {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200203 if (IsPlusInfinity() || other.IsMinusInfinity()) {
204 RTC_DCHECK(!IsMinusInfinity());
205 RTC_DCHECK(!other.IsPlusInfinity());
206 return PlusInfinity();
207 } else if (IsMinusInfinity() || other.IsPlusInfinity()) {
208 RTC_DCHECK(!IsPlusInfinity());
209 RTC_DCHECK(!other.IsMinusInfinity());
210 return MinusInfinity();
211 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200212 return TimeDelta::us(us() - other.us());
213 }
214 TimeDelta& operator-=(const TimeDelta& other) {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200215 *this = *this - other;
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200216 return *this;
217 }
218 TimeDelta& operator+=(const TimeDelta& other) {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200219 *this = *this + other;
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200220 return *this;
221 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200222 constexpr double operator/(const TimeDelta& other) const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200223 return us<double>() / other.us<double>();
224 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200225 constexpr bool operator==(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200226 return microseconds_ == other.microseconds_;
227 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200228 constexpr bool operator!=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200229 return microseconds_ != other.microseconds_;
230 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200231 constexpr bool operator<=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200232 return microseconds_ <= other.microseconds_;
233 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200234 constexpr bool operator>=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200235 return microseconds_ >= other.microseconds_;
236 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200237 constexpr bool operator>(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200238 return microseconds_ > other.microseconds_;
239 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200240 constexpr bool operator<(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200241 return microseconds_ < other.microseconds_;
242 }
243
244 private:
Sebastian Jansson8e064192018-08-07 12:34:33 +0200245 explicit constexpr TimeDelta(int64_t us) : microseconds_(us) {}
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200246 constexpr int64_t UnsafeSeconds() const {
247 return (microseconds_ + (microseconds_ >= 0 ? 500000 : -500000)) / 1000000;
248 }
249 constexpr int64_t UnsafeMillis() const {
250 return (microseconds_ + (microseconds_ >= 0 ? 500 : -500)) / 1000;
251 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200252 int64_t microseconds_;
253};
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200254
255inline TimeDelta operator*(const TimeDelta& delta, const double& scalar) {
256 return TimeDelta::us(std::round(delta.us() * scalar));
257}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200258inline TimeDelta operator*(const double& scalar, const TimeDelta& delta) {
259 return delta * scalar;
260}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200261inline TimeDelta operator*(const TimeDelta& delta, const int64_t& scalar) {
262 return TimeDelta::us(delta.us() * scalar);
263}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200264inline TimeDelta operator*(const int64_t& scalar, const TimeDelta& delta) {
265 return delta * scalar;
266}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200267inline TimeDelta operator*(const TimeDelta& delta, const int32_t& scalar) {
268 return TimeDelta::us(delta.us() * scalar);
269}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200270inline TimeDelta operator*(const int32_t& scalar, const TimeDelta& delta) {
271 return delta * scalar;
272}
273
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200274inline TimeDelta operator/(const TimeDelta& delta, const int64_t& scalar) {
275 return TimeDelta::us(delta.us() / scalar);
276}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200277std::string ToString(const TimeDelta& value);
Sebastian Jansson2afd2812018-08-23 14:44:05 +0200278
279#ifdef UNIT_TEST
280inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
281 std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
282 TimeDelta value) {
283 return stream << ToString(value);
284}
285#endif // UNIT_TEST
286
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200287} // namespace webrtc
288
Sebastian Jansson6fae6ec2018-05-08 10:43:18 +0200289#endif // API_UNITS_TIME_DELTA_H_