blob: 7196bae34627993fa2a32628c45d565dfbeb8f6c [file] [log] [blame]
Sebastian Jansson72bba622018-11-19 11:17:12 +01001/*
2 * Copyright 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#ifndef RTC_BASE_UNITS_UNIT_BASE_H_
11#define RTC_BASE_UNITS_UNIT_BASE_H_
12
13#include <stdint.h>
Jonas Olssona4d87372019-07-05 19:08:33 +020014
Sebastian Jansson72bba622018-11-19 11:17:12 +010015#include <algorithm>
16#include <cmath>
17#include <limits>
18#include <type_traits>
19
20#include "rtc_base/checks.h"
21#include "rtc_base/numerics/safe_conversions.h"
22
23namespace webrtc {
24namespace rtc_units_impl {
25
26// UnitBase is a base class for implementing custom value types with a specific
Bjorn Terelius18f65dc2019-01-25 11:05:04 +010027// unit. It provides type safety and commonly useful operations. The underlying
Sebastian Jansson72bba622018-11-19 11:17:12 +010028// storage is always an int64_t, it's up to the unit implementation to choose
29// what scale it represents.
30//
31// It's used like:
32// class MyUnit: public UnitBase<MyUnit> {...};
33//
34// Unit_T is the subclass representing the specific unit.
35template <class Unit_T>
36class UnitBase {
37 public:
38 UnitBase() = delete;
39 static constexpr Unit_T Zero() { return Unit_T(0); }
40 static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); }
41 static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); }
42
43 constexpr bool IsZero() const { return value_ == 0; }
44 constexpr bool IsFinite() const { return !IsInfinite(); }
45 constexpr bool IsInfinite() const {
46 return value_ == PlusInfinityVal() || value_ == MinusInfinityVal();
47 }
48 constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); }
49 constexpr bool IsMinusInfinity() const {
50 return value_ == MinusInfinityVal();
51 }
52
53 constexpr bool operator==(const Unit_T& other) const {
54 return value_ == other.value_;
55 }
56 constexpr bool operator!=(const Unit_T& other) const {
57 return value_ != other.value_;
58 }
59 constexpr bool operator<=(const Unit_T& other) const {
60 return value_ <= other.value_;
61 }
62 constexpr bool operator>=(const Unit_T& other) const {
63 return value_ >= other.value_;
64 }
65 constexpr bool operator>(const Unit_T& other) const {
66 return value_ > other.value_;
67 }
68 constexpr bool operator<(const Unit_T& other) const {
69 return value_ < other.value_;
70 }
Sebastian Janssond7fade52020-01-29 10:44:51 +010071 constexpr Unit_T RoundTo(const Unit_T& resolution) const {
Sebastian Jansson26b5e352019-06-07 11:05:31 +020072 RTC_DCHECK(IsFinite());
73 RTC_DCHECK(resolution.IsFinite());
74 RTC_DCHECK_GT(resolution.value_, 0);
75 return Unit_T((value_ + resolution.value_ / 2) / resolution.value_) *
76 resolution.value_;
77 }
Sebastian Janssond7fade52020-01-29 10:44:51 +010078 constexpr Unit_T RoundUpTo(const Unit_T& resolution) const {
Sebastian Jansson26b5e352019-06-07 11:05:31 +020079 RTC_DCHECK(IsFinite());
80 RTC_DCHECK(resolution.IsFinite());
81 RTC_DCHECK_GT(resolution.value_, 0);
82 return Unit_T((value_ + resolution.value_ - 1) / resolution.value_) *
83 resolution.value_;
84 }
Sebastian Janssond7fade52020-01-29 10:44:51 +010085 constexpr Unit_T RoundDownTo(const Unit_T& resolution) const {
Sebastian Jansson26b5e352019-06-07 11:05:31 +020086 RTC_DCHECK(IsFinite());
87 RTC_DCHECK(resolution.IsFinite());
88 RTC_DCHECK_GT(resolution.value_, 0);
89 return Unit_T(value_ / resolution.value_) * resolution.value_;
90 }
Sebastian Jansson72bba622018-11-19 11:17:12 +010091
92 protected:
Sebastian Jansson72bba622018-11-19 11:17:12 +010093 template <
94 typename T,
95 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
Danil Chapovalov7356a562020-01-20 13:02:44 +010096 static constexpr Unit_T FromValue(T value) {
Sebastian Jansson72bba622018-11-19 11:17:12 +010097 if (Unit_T::one_sided)
98 RTC_DCHECK_GE(value, 0);
99 RTC_DCHECK_GT(value, MinusInfinityVal());
100 RTC_DCHECK_LT(value, PlusInfinityVal());
101 return Unit_T(rtc::dchecked_cast<int64_t>(value));
102 }
103 template <typename T,
104 typename std::enable_if<std::is_floating_point<T>::value>::type* =
105 nullptr>
Danil Chapovalov7356a562020-01-20 13:02:44 +0100106 static constexpr Unit_T FromValue(T value) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100107 if (value == std::numeric_limits<T>::infinity()) {
108 return PlusInfinity();
109 } else if (value == -std::numeric_limits<T>::infinity()) {
110 return MinusInfinity();
111 } else {
112 RTC_DCHECK(!std::isnan(value));
113 return FromValue(rtc::dchecked_cast<int64_t>(value));
114 }
115 }
116
117 template <
Sebastian Jansson72bba622018-11-19 11:17:12 +0100118 typename T,
119 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
Danil Chapovalov7356a562020-01-20 13:02:44 +0100120 static constexpr Unit_T FromFraction(int64_t denominator, T value) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100121 if (Unit_T::one_sided)
122 RTC_DCHECK_GE(value, 0);
Danil Chapovalov7356a562020-01-20 13:02:44 +0100123 RTC_DCHECK_GT(value, MinusInfinityVal() / denominator);
124 RTC_DCHECK_LT(value, PlusInfinityVal() / denominator);
125 return Unit_T(rtc::dchecked_cast<int64_t>(value * denominator));
Sebastian Jansson72bba622018-11-19 11:17:12 +0100126 }
Danil Chapovalov7356a562020-01-20 13:02:44 +0100127 template <typename T,
Sebastian Jansson72bba622018-11-19 11:17:12 +0100128 typename std::enable_if<std::is_floating_point<T>::value>::type* =
129 nullptr>
Danil Chapovalov7356a562020-01-20 13:02:44 +0100130 static constexpr Unit_T FromFraction(int64_t denominator, T value) {
131 return FromValue(value * denominator);
Sebastian Jansson72bba622018-11-19 11:17:12 +0100132 }
133
134 template <typename T = int64_t>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100135 constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
136 ToValue() const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100137 RTC_DCHECK(IsFinite());
138 return rtc::dchecked_cast<T>(value_);
139 }
140 template <typename T>
141 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
142 ToValue() const {
143 return IsPlusInfinity()
144 ? std::numeric_limits<T>::infinity()
145 : IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
146 : value_;
147 }
148 template <typename T>
149 constexpr T ToValueOr(T fallback_value) const {
150 return IsFinite() ? value_ : fallback_value;
151 }
152
153 template <int64_t Denominator, typename T = int64_t>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100154 constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
155 ToFraction() const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100156 RTC_DCHECK(IsFinite());
157 if (Unit_T::one_sided) {
158 return rtc::dchecked_cast<T>(
159 DivRoundPositiveToNearest(value_, Denominator));
160 } else {
161 return rtc::dchecked_cast<T>(DivRoundToNearest(value_, Denominator));
162 }
163 }
164 template <int64_t Denominator, typename T>
165 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
166 ToFraction() const {
167 return ToValue<T>() * (1 / static_cast<T>(Denominator));
168 }
169
170 template <int64_t Denominator>
171 constexpr int64_t ToFractionOr(int64_t fallback_value) const {
172 return IsFinite() ? Unit_T::one_sided
173 ? DivRoundPositiveToNearest(value_, Denominator)
174 : DivRoundToNearest(value_, Denominator)
175 : fallback_value;
176 }
177
178 template <int64_t Factor, typename T = int64_t>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100179 constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
180 ToMultiple() const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100181 RTC_DCHECK_GE(ToValue(), std::numeric_limits<T>::min() / Factor);
182 RTC_DCHECK_LE(ToValue(), std::numeric_limits<T>::max() / Factor);
183 return rtc::dchecked_cast<T>(ToValue() * Factor);
184 }
185 template <int64_t Factor, typename T>
186 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
187 ToMultiple() const {
188 return ToValue<T>() * Factor;
189 }
190
191 explicit constexpr UnitBase(int64_t value) : value_(value) {}
192
193 private:
194 template <class RelativeUnit_T>
195 friend class RelativeUnit;
196
197 static inline constexpr int64_t PlusInfinityVal() {
198 return std::numeric_limits<int64_t>::max();
199 }
200 static inline constexpr int64_t MinusInfinityVal() {
201 return std::numeric_limits<int64_t>::min();
202 }
203
Sebastian Janssond7fade52020-01-29 10:44:51 +0100204 constexpr Unit_T& AsSubClassRef() { return static_cast<Unit_T&>(*this); }
Sebastian Jansson72bba622018-11-19 11:17:12 +0100205 constexpr const Unit_T& AsSubClassRef() const {
Sebastian Janssond7fade52020-01-29 10:44:51 +0100206 return static_cast<const Unit_T&>(*this);
Sebastian Jansson72bba622018-11-19 11:17:12 +0100207 }
208 // Assumes that n >= 0 and d > 0.
209 static constexpr int64_t DivRoundPositiveToNearest(int64_t n, int64_t d) {
210 return (n + d / 2) / d;
211 }
212 // Assumes that d > 0.
213 static constexpr int64_t DivRoundToNearest(int64_t n, int64_t d) {
214 return (n + (n >= 0 ? d / 2 : -d / 2)) / d;
215 }
216
217 int64_t value_;
218};
219
220// Extends UnitBase to provide operations for relative units, that is, units
221// that have a meaningful relation between values such that a += b is a
222// sensible thing to do. For a,b <- same unit.
223template <class Unit_T>
224class RelativeUnit : public UnitBase<Unit_T> {
225 public:
Sebastian Janssond7fade52020-01-29 10:44:51 +0100226 constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100227 return std::max(min_value,
228 std::min(UnitBase<Unit_T>::AsSubClassRef(), max_value));
229 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100230 constexpr void Clamp(Unit_T min_value, Unit_T max_value) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100231 *this = Clamped(min_value, max_value);
232 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100233 constexpr Unit_T operator+(const Unit_T other) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100234 if (this->IsPlusInfinity() || other.IsPlusInfinity()) {
235 RTC_DCHECK(!this->IsMinusInfinity());
236 RTC_DCHECK(!other.IsMinusInfinity());
237 return this->PlusInfinity();
238 } else if (this->IsMinusInfinity() || other.IsMinusInfinity()) {
239 RTC_DCHECK(!this->IsPlusInfinity());
240 RTC_DCHECK(!other.IsPlusInfinity());
241 return this->MinusInfinity();
242 }
243 return UnitBase<Unit_T>::FromValue(this->ToValue() + other.ToValue());
244 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100245 constexpr Unit_T operator-(const Unit_T other) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100246 if (this->IsPlusInfinity() || other.IsMinusInfinity()) {
247 RTC_DCHECK(!this->IsMinusInfinity());
248 RTC_DCHECK(!other.IsPlusInfinity());
249 return this->PlusInfinity();
250 } else if (this->IsMinusInfinity() || other.IsPlusInfinity()) {
251 RTC_DCHECK(!this->IsPlusInfinity());
252 RTC_DCHECK(!other.IsMinusInfinity());
253 return this->MinusInfinity();
254 }
255 return UnitBase<Unit_T>::FromValue(this->ToValue() - other.ToValue());
256 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100257 constexpr Unit_T& operator+=(const Unit_T other) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100258 *this = *this + other;
259 return this->AsSubClassRef();
260 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100261 constexpr Unit_T& operator-=(const Unit_T other) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100262 *this = *this - other;
263 return this->AsSubClassRef();
264 }
265 constexpr double operator/(const Unit_T other) const {
266 return UnitBase<Unit_T>::template ToValue<double>() /
267 other.template ToValue<double>();
268 }
269 template <typename T>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100270 constexpr typename std::enable_if<std::is_arithmetic<T>::value, Unit_T>::type
271 operator/(const T& scalar) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100272 return UnitBase<Unit_T>::FromValue(
273 std::round(UnitBase<Unit_T>::template ToValue<int64_t>() / scalar));
274 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100275 constexpr Unit_T operator*(double scalar) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100276 return UnitBase<Unit_T>::FromValue(std::round(this->ToValue() * scalar));
277 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100278 constexpr Unit_T operator*(int64_t scalar) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100279 return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
280 }
Sebastian Janssond7fade52020-01-29 10:44:51 +0100281 constexpr Unit_T operator*(int32_t scalar) const {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100282 return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
283 }
284
285 protected:
286 using UnitBase<Unit_T>::UnitBase;
287};
288
289template <class Unit_T>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100290inline constexpr Unit_T operator*(double scalar, RelativeUnit<Unit_T> other) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100291 return other * scalar;
292}
293template <class Unit_T>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100294inline constexpr Unit_T operator*(int64_t scalar, RelativeUnit<Unit_T> other) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100295 return other * scalar;
296}
297template <class Unit_T>
Sebastian Janssond7fade52020-01-29 10:44:51 +0100298inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit<Unit_T> other) {
Sebastian Jansson72bba622018-11-19 11:17:12 +0100299 return other * scalar;
300}
301
302} // namespace rtc_units_impl
303
304} // namespace webrtc
305
306#endif // RTC_BASE_UNITS_UNIT_BASE_H_