blob: 74b5385128a9b1213f1a3f883eceae840a12826b [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>
Yves Gerey988cc082018-10-23 12:03:01 +020023#include <type_traits>
Sebastian Jansson30bd4032018-04-13 13:56:17 +020024
25#include "rtc_base/checks.h"
Sebastian Jansson942b3602018-05-30 15:47:44 +020026#include "rtc_base/numerics/safe_conversions.h"
Sebastian Jansson30bd4032018-04-13 13:56:17 +020027
28namespace webrtc {
29namespace timedelta_impl {
30constexpr int64_t kPlusInfinityVal = std::numeric_limits<int64_t>::max();
31constexpr int64_t kMinusInfinityVal = std::numeric_limits<int64_t>::min();
Sebastian Jansson30bd4032018-04-13 13:56:17 +020032} // namespace timedelta_impl
33
34// TimeDelta represents the difference between two timestamps. Commonly this can
35// be a duration. However since two Timestamps are not guaranteed to have the
36// same epoch (they might come from different computers, making exact
37// synchronisation infeasible), the duration covered by a TimeDelta can be
38// undefined. To simplify usage, it can be constructed and converted to
39// different units, specifically seconds (s), milliseconds (ms) and
40// microseconds (us).
41class TimeDelta {
42 public:
Sebastian Jansson3b69b192018-05-07 13:51:51 +020043 TimeDelta() = delete;
Sebastian Jansson8e064192018-08-07 12:34:33 +020044 static constexpr TimeDelta Zero() { return TimeDelta(0); }
45 static constexpr TimeDelta PlusInfinity() {
Sebastian Jansson30bd4032018-04-13 13:56:17 +020046 return TimeDelta(timedelta_impl::kPlusInfinityVal);
47 }
Sebastian Jansson8e064192018-08-07 12:34:33 +020048 static constexpr TimeDelta MinusInfinity() {
Sebastian Jansson30bd4032018-04-13 13:56:17 +020049 return TimeDelta(timedelta_impl::kMinusInfinityVal);
50 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +020051 template <int64_t seconds>
52 static constexpr TimeDelta Seconds() {
53 static_assert(seconds > timedelta_impl::kMinusInfinityVal / 1000000, "");
54 static_assert(seconds < timedelta_impl::kPlusInfinityVal / 1000000, "");
55 return TimeDelta(seconds * 1000000);
56 }
57 template <int64_t ms>
58 static constexpr TimeDelta Millis() {
59 static_assert(ms > timedelta_impl::kMinusInfinityVal / 1000, "");
60 static_assert(ms < timedelta_impl::kPlusInfinityVal / 1000, "");
61 return TimeDelta(ms * 1000);
62 }
63 template <int64_t us>
64 static constexpr TimeDelta Micros() {
65 static_assert(us > timedelta_impl::kMinusInfinityVal, "");
66 static_assert(us < timedelta_impl::kPlusInfinityVal, "");
67 return TimeDelta(us);
68 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020069
70 template <
71 typename T,
72 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
73 static TimeDelta seconds(T seconds) {
74 RTC_DCHECK_GT(seconds, timedelta_impl::kMinusInfinityVal / 1000000);
75 RTC_DCHECK_LT(seconds, timedelta_impl::kPlusInfinityVal / 1000000);
76 return TimeDelta(rtc::dchecked_cast<int64_t>(seconds) * 1000000);
Sebastian Jansson30bd4032018-04-13 13:56:17 +020077 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020078 template <
79 typename T,
80 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
81 static TimeDelta ms(T milliseconds) {
82 RTC_DCHECK_GT(milliseconds, timedelta_impl::kMinusInfinityVal / 1000);
83 RTC_DCHECK_LT(milliseconds, timedelta_impl::kPlusInfinityVal / 1000);
84 return TimeDelta(rtc::dchecked_cast<int64_t>(milliseconds) * 1000);
Sebastian Jansson30bd4032018-04-13 13:56:17 +020085 }
Sebastian Jansson942b3602018-05-30 15:47:44 +020086 template <
87 typename T,
88 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
89 static TimeDelta us(T microseconds) {
90 RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
91 RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
92 return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
Sebastian Jansson5f83cf02018-05-08 14:52:22 +020093 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +020094
Sebastian Jansson942b3602018-05-30 15:47:44 +020095 template <typename T,
96 typename std::enable_if<std::is_floating_point<T>::value>::type* =
97 nullptr>
98 static TimeDelta seconds(T seconds) {
99 return TimeDelta::us(seconds * 1e6);
100 }
101 template <typename T,
102 typename std::enable_if<std::is_floating_point<T>::value>::type* =
103 nullptr>
104 static TimeDelta ms(T milliseconds) {
105 return TimeDelta::us(milliseconds * 1e3);
106 }
107 template <typename T,
108 typename std::enable_if<std::is_floating_point<T>::value>::type* =
109 nullptr>
110 static TimeDelta us(T microseconds) {
111 if (microseconds == std::numeric_limits<T>::infinity()) {
112 return PlusInfinity();
113 } else if (microseconds == -std::numeric_limits<T>::infinity()) {
114 return MinusInfinity();
115 } else {
116 RTC_DCHECK(!std::isnan(microseconds));
117 RTC_DCHECK_GT(microseconds, timedelta_impl::kMinusInfinityVal);
118 RTC_DCHECK_LT(microseconds, timedelta_impl::kPlusInfinityVal);
119 return TimeDelta(rtc::dchecked_cast<int64_t>(microseconds));
120 }
121 }
122
123 template <typename T = int64_t>
124 typename std::enable_if<std::is_integral<T>::value, T>::type seconds() const {
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200125 RTC_DCHECK(IsFinite());
126 return rtc::dchecked_cast<T>(UnsafeSeconds());
Sebastian Jansson942b3602018-05-30 15:47:44 +0200127 }
128 template <typename T = int64_t>
129 typename std::enable_if<std::is_integral<T>::value, T>::type ms() const {
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200130 RTC_DCHECK(IsFinite());
131 return rtc::dchecked_cast<T>(UnsafeMillis());
Sebastian Jansson942b3602018-05-30 15:47:44 +0200132 }
133 template <typename T = int64_t>
134 typename std::enable_if<std::is_integral<T>::value, T>::type us() const {
135 RTC_DCHECK(IsFinite());
136 return rtc::dchecked_cast<T>(microseconds_);
137 }
138 template <typename T = int64_t>
139 typename std::enable_if<std::is_integral<T>::value, T>::type ns() const {
140 RTC_DCHECK_GE(us(), std::numeric_limits<T>::min() / 1000);
141 RTC_DCHECK_LE(us(), std::numeric_limits<T>::max() / 1000);
142 return rtc::dchecked_cast<T>(us() * 1000);
143 }
144
145 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200146 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
147 seconds() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200148 return us<T>() * 1e-6;
149 }
150 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200151 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
152 ms() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200153 return us<T>() * 1e-3;
154 }
155 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200156 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
157 us() const {
158 return IsPlusInfinity()
159 ? std::numeric_limits<T>::infinity()
160 : IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
161 : microseconds_;
Sebastian Jansson942b3602018-05-30 15:47:44 +0200162 }
163 template <typename T>
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200164 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
165 ns() const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200166 return us<T>() * 1e3;
167 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200168
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200169 constexpr int64_t seconds_or(int64_t fallback_value) const {
170 return IsFinite() ? UnsafeSeconds() : fallback_value;
171 }
172 constexpr int64_t ms_or(int64_t fallback_value) const {
173 return IsFinite() ? UnsafeMillis() : fallback_value;
174 }
175 constexpr int64_t us_or(int64_t fallback_value) const {
176 return IsFinite() ? microseconds_ : fallback_value;
177 }
178
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200179 TimeDelta Abs() const { return TimeDelta::us(std::abs(us())); }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200180 constexpr bool IsZero() const { return microseconds_ == 0; }
181 constexpr bool IsFinite() const { return !IsInfinite(); }
182 constexpr bool IsInfinite() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200183 return microseconds_ == timedelta_impl::kPlusInfinityVal ||
184 microseconds_ == timedelta_impl::kMinusInfinityVal;
185 }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200186 constexpr bool IsPlusInfinity() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200187 return microseconds_ == timedelta_impl::kPlusInfinityVal;
188 }
Sebastian Jansson8e064192018-08-07 12:34:33 +0200189 constexpr bool IsMinusInfinity() const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200190 return microseconds_ == timedelta_impl::kMinusInfinityVal;
191 }
192 TimeDelta operator+(const TimeDelta& other) const {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200193 if (IsPlusInfinity() || other.IsPlusInfinity()) {
194 RTC_DCHECK(!IsMinusInfinity());
195 RTC_DCHECK(!other.IsMinusInfinity());
196 return PlusInfinity();
197 } else if (IsMinusInfinity() || other.IsMinusInfinity()) {
198 RTC_DCHECK(!IsPlusInfinity());
199 RTC_DCHECK(!other.IsPlusInfinity());
200 return MinusInfinity();
201 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200202 return TimeDelta::us(us() + other.us());
203 }
204 TimeDelta operator-(const TimeDelta& other) const {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200205 if (IsPlusInfinity() || other.IsMinusInfinity()) {
206 RTC_DCHECK(!IsMinusInfinity());
207 RTC_DCHECK(!other.IsPlusInfinity());
208 return PlusInfinity();
209 } else if (IsMinusInfinity() || other.IsPlusInfinity()) {
210 RTC_DCHECK(!IsPlusInfinity());
211 RTC_DCHECK(!other.IsMinusInfinity());
212 return MinusInfinity();
213 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200214 return TimeDelta::us(us() - other.us());
215 }
216 TimeDelta& operator-=(const TimeDelta& other) {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200217 *this = *this - other;
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200218 return *this;
219 }
220 TimeDelta& operator+=(const TimeDelta& other) {
Sebastian Jansson88c1a9e2018-08-30 13:58:38 +0200221 *this = *this + other;
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200222 return *this;
223 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200224 constexpr double operator/(const TimeDelta& other) const {
Sebastian Jansson942b3602018-05-30 15:47:44 +0200225 return us<double>() / other.us<double>();
226 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200227 constexpr bool operator==(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200228 return microseconds_ == other.microseconds_;
229 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200230 constexpr bool operator!=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200231 return microseconds_ != other.microseconds_;
232 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200233 constexpr bool operator<=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200234 return microseconds_ <= other.microseconds_;
235 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200236 constexpr bool operator>=(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200237 return microseconds_ >= other.microseconds_;
238 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200239 constexpr bool operator>(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200240 return microseconds_ > other.microseconds_;
241 }
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200242 constexpr bool operator<(const TimeDelta& other) const {
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200243 return microseconds_ < other.microseconds_;
244 }
245
246 private:
Sebastian Jansson8e064192018-08-07 12:34:33 +0200247 explicit constexpr TimeDelta(int64_t us) : microseconds_(us) {}
Sebastian Janssonc1c8b8e2018-08-07 15:29:04 +0200248 constexpr int64_t UnsafeSeconds() const {
249 return (microseconds_ + (microseconds_ >= 0 ? 500000 : -500000)) / 1000000;
250 }
251 constexpr int64_t UnsafeMillis() const {
252 return (microseconds_ + (microseconds_ >= 0 ? 500 : -500)) / 1000;
253 }
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200254 int64_t microseconds_;
255};
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200256
257inline TimeDelta operator*(const TimeDelta& delta, const double& scalar) {
258 return TimeDelta::us(std::round(delta.us() * scalar));
259}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200260inline TimeDelta operator*(const double& scalar, const TimeDelta& delta) {
261 return delta * scalar;
262}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200263inline TimeDelta operator*(const TimeDelta& delta, const int64_t& scalar) {
264 return TimeDelta::us(delta.us() * scalar);
265}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200266inline TimeDelta operator*(const int64_t& scalar, const TimeDelta& delta) {
267 return delta * scalar;
268}
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200269inline TimeDelta operator*(const TimeDelta& delta, const int32_t& scalar) {
270 return TimeDelta::us(delta.us() * scalar);
271}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200272inline TimeDelta operator*(const int32_t& scalar, const TimeDelta& delta) {
273 return delta * scalar;
274}
275
Sebastian Jansson66fa5352018-04-30 16:54:57 +0200276inline TimeDelta operator/(const TimeDelta& delta, const int64_t& scalar) {
277 return TimeDelta::us(delta.us() / scalar);
278}
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200279std::string ToString(const TimeDelta& value);
Sebastian Jansson2afd2812018-08-23 14:44:05 +0200280
281#ifdef UNIT_TEST
282inline std::ostream& operator<<( // no-presubmit-check TODO(webrtc:8982)
283 std::ostream& stream, // no-presubmit-check TODO(webrtc:8982)
284 TimeDelta value) {
285 return stream << ToString(value);
286}
287#endif // UNIT_TEST
288
Sebastian Jansson30bd4032018-04-13 13:56:17 +0200289} // namespace webrtc
290
Sebastian Jansson6fae6ec2018-05-08 10:43:18 +0200291#endif // API_UNITS_TIME_DELTA_H_