blob: 5503a329937667642cfa70f7a17d29e333b4f064 [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>
14#include <algorithm>
15#include <cmath>
16#include <limits>
17#include <type_traits>
18
19#include "rtc_base/checks.h"
20#include "rtc_base/numerics/safe_conversions.h"
21
22namespace webrtc {
23namespace rtc_units_impl {
24
25// UnitBase is a base class for implementing custom value types with a specific
26// unit. It provides type safety and sommonly useful operations. The undelying
27// storage is always an int64_t, it's up to the unit implementation to choose
28// what scale it represents.
29//
30// It's used like:
31// class MyUnit: public UnitBase<MyUnit> {...};
32//
33// Unit_T is the subclass representing the specific unit.
34template <class Unit_T>
35class UnitBase {
36 public:
37 UnitBase() = delete;
38 static constexpr Unit_T Zero() { return Unit_T(0); }
39 static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); }
40 static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); }
41
42 constexpr bool IsZero() const { return value_ == 0; }
43 constexpr bool IsFinite() const { return !IsInfinite(); }
44 constexpr bool IsInfinite() const {
45 return value_ == PlusInfinityVal() || value_ == MinusInfinityVal();
46 }
47 constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); }
48 constexpr bool IsMinusInfinity() const {
49 return value_ == MinusInfinityVal();
50 }
51
52 constexpr bool operator==(const Unit_T& other) const {
53 return value_ == other.value_;
54 }
55 constexpr bool operator!=(const Unit_T& other) const {
56 return value_ != other.value_;
57 }
58 constexpr bool operator<=(const Unit_T& other) const {
59 return value_ <= other.value_;
60 }
61 constexpr bool operator>=(const Unit_T& other) const {
62 return value_ >= other.value_;
63 }
64 constexpr bool operator>(const Unit_T& other) const {
65 return value_ > other.value_;
66 }
67 constexpr bool operator<(const Unit_T& other) const {
68 return value_ < other.value_;
69 }
70
71 protected:
72 template <int64_t value>
73 static constexpr Unit_T FromStaticValue() {
74 static_assert(value >= 0 || !Unit_T::one_sided, "");
75 static_assert(value > MinusInfinityVal(), "");
76 static_assert(value < PlusInfinityVal(), "");
77 return Unit_T(value);
78 }
79
80 template <int64_t fraction_value, int64_t Denominator>
81 static constexpr Unit_T FromStaticFraction() {
82 static_assert(fraction_value >= 0 || !Unit_T::one_sided, "");
83 static_assert(fraction_value > MinusInfinityVal() / Denominator, "");
84 static_assert(fraction_value < PlusInfinityVal() / Denominator, "");
85 return Unit_T(fraction_value * Denominator);
86 }
87
88 template <
89 typename T,
90 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
91 static Unit_T FromValue(T value) {
92 if (Unit_T::one_sided)
93 RTC_DCHECK_GE(value, 0);
94 RTC_DCHECK_GT(value, MinusInfinityVal());
95 RTC_DCHECK_LT(value, PlusInfinityVal());
96 return Unit_T(rtc::dchecked_cast<int64_t>(value));
97 }
98 template <typename T,
99 typename std::enable_if<std::is_floating_point<T>::value>::type* =
100 nullptr>
101 static Unit_T FromValue(T value) {
102 if (value == std::numeric_limits<T>::infinity()) {
103 return PlusInfinity();
104 } else if (value == -std::numeric_limits<T>::infinity()) {
105 return MinusInfinity();
106 } else {
107 RTC_DCHECK(!std::isnan(value));
108 return FromValue(rtc::dchecked_cast<int64_t>(value));
109 }
110 }
111
112 template <
113 int64_t Denominator,
114 typename T,
115 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
116 static Unit_T FromFraction(T value) {
117 if (Unit_T::one_sided)
118 RTC_DCHECK_GE(value, 0);
119 RTC_DCHECK_GT(value, MinusInfinityVal() / Denominator);
120 RTC_DCHECK_LT(value, PlusInfinityVal() / Denominator);
121 return Unit_T(rtc::dchecked_cast<int64_t>(value * Denominator));
122 }
123 template <int64_t Denominator,
124 typename T,
125 typename std::enable_if<std::is_floating_point<T>::value>::type* =
126 nullptr>
127 static Unit_T FromFraction(T value) {
128 return FromValue(value * Denominator);
129 }
130
131 template <typename T = int64_t>
132 typename std::enable_if<std::is_integral<T>::value, T>::type ToValue() const {
133 RTC_DCHECK(IsFinite());
134 return rtc::dchecked_cast<T>(value_);
135 }
136 template <typename T>
137 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
138 ToValue() const {
139 return IsPlusInfinity()
140 ? std::numeric_limits<T>::infinity()
141 : IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
142 : value_;
143 }
144 template <typename T>
145 constexpr T ToValueOr(T fallback_value) const {
146 return IsFinite() ? value_ : fallback_value;
147 }
148
149 template <int64_t Denominator, typename T = int64_t>
150 typename std::enable_if<std::is_integral<T>::value, T>::type ToFraction()
151 const {
152 RTC_DCHECK(IsFinite());
153 if (Unit_T::one_sided) {
154 return rtc::dchecked_cast<T>(
155 DivRoundPositiveToNearest(value_, Denominator));
156 } else {
157 return rtc::dchecked_cast<T>(DivRoundToNearest(value_, Denominator));
158 }
159 }
160 template <int64_t Denominator, typename T>
161 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
162 ToFraction() const {
163 return ToValue<T>() * (1 / static_cast<T>(Denominator));
164 }
165
166 template <int64_t Denominator>
167 constexpr int64_t ToFractionOr(int64_t fallback_value) const {
168 return IsFinite() ? Unit_T::one_sided
169 ? DivRoundPositiveToNearest(value_, Denominator)
170 : DivRoundToNearest(value_, Denominator)
171 : fallback_value;
172 }
173
174 template <int64_t Factor, typename T = int64_t>
175 typename std::enable_if<std::is_integral<T>::value, T>::type ToMultiple()
176 const {
177 RTC_DCHECK_GE(ToValue(), std::numeric_limits<T>::min() / Factor);
178 RTC_DCHECK_LE(ToValue(), std::numeric_limits<T>::max() / Factor);
179 return rtc::dchecked_cast<T>(ToValue() * Factor);
180 }
181 template <int64_t Factor, typename T>
182 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
183 ToMultiple() const {
184 return ToValue<T>() * Factor;
185 }
186
187 explicit constexpr UnitBase(int64_t value) : value_(value) {}
188
189 private:
190 template <class RelativeUnit_T>
191 friend class RelativeUnit;
192
193 static inline constexpr int64_t PlusInfinityVal() {
194 return std::numeric_limits<int64_t>::max();
195 }
196 static inline constexpr int64_t MinusInfinityVal() {
197 return std::numeric_limits<int64_t>::min();
198 }
199
200 Unit_T& AsSubClassRef() { return reinterpret_cast<Unit_T&>(*this); }
201 constexpr const Unit_T& AsSubClassRef() const {
202 return reinterpret_cast<const Unit_T&>(*this);
203 }
204 // Assumes that n >= 0 and d > 0.
205 static constexpr int64_t DivRoundPositiveToNearest(int64_t n, int64_t d) {
206 return (n + d / 2) / d;
207 }
208 // Assumes that d > 0.
209 static constexpr int64_t DivRoundToNearest(int64_t n, int64_t d) {
210 return (n + (n >= 0 ? d / 2 : -d / 2)) / d;
211 }
212
213 int64_t value_;
214};
215
216// Extends UnitBase to provide operations for relative units, that is, units
217// that have a meaningful relation between values such that a += b is a
218// sensible thing to do. For a,b <- same unit.
219template <class Unit_T>
220class RelativeUnit : public UnitBase<Unit_T> {
221 public:
222 Unit_T Clamped(Unit_T min_value, Unit_T max_value) const {
223 return std::max(min_value,
224 std::min(UnitBase<Unit_T>::AsSubClassRef(), max_value));
225 }
226 void Clamp(Unit_T min_value, Unit_T max_value) {
227 *this = Clamped(min_value, max_value);
228 }
229 Unit_T operator+(const Unit_T other) const {
230 if (this->IsPlusInfinity() || other.IsPlusInfinity()) {
231 RTC_DCHECK(!this->IsMinusInfinity());
232 RTC_DCHECK(!other.IsMinusInfinity());
233 return this->PlusInfinity();
234 } else if (this->IsMinusInfinity() || other.IsMinusInfinity()) {
235 RTC_DCHECK(!this->IsPlusInfinity());
236 RTC_DCHECK(!other.IsPlusInfinity());
237 return this->MinusInfinity();
238 }
239 return UnitBase<Unit_T>::FromValue(this->ToValue() + other.ToValue());
240 }
241 Unit_T operator-(const Unit_T other) const {
242 if (this->IsPlusInfinity() || other.IsMinusInfinity()) {
243 RTC_DCHECK(!this->IsMinusInfinity());
244 RTC_DCHECK(!other.IsPlusInfinity());
245 return this->PlusInfinity();
246 } else if (this->IsMinusInfinity() || other.IsPlusInfinity()) {
247 RTC_DCHECK(!this->IsPlusInfinity());
248 RTC_DCHECK(!other.IsMinusInfinity());
249 return this->MinusInfinity();
250 }
251 return UnitBase<Unit_T>::FromValue(this->ToValue() - other.ToValue());
252 }
253 Unit_T& operator+=(const Unit_T other) {
254 *this = *this + other;
255 return this->AsSubClassRef();
256 }
257 Unit_T& operator-=(const Unit_T other) {
258 *this = *this - other;
259 return this->AsSubClassRef();
260 }
261 constexpr double operator/(const Unit_T other) const {
262 return UnitBase<Unit_T>::template ToValue<double>() /
263 other.template ToValue<double>();
264 }
265 template <typename T>
266 typename std::enable_if<std::is_arithmetic<T>::value, Unit_T>::type operator/(
267 const T& scalar) const {
268 return UnitBase<Unit_T>::FromValue(
269 std::round(UnitBase<Unit_T>::template ToValue<int64_t>() / scalar));
270 }
271 Unit_T operator*(const double scalar) const {
272 return UnitBase<Unit_T>::FromValue(std::round(this->ToValue() * scalar));
273 }
274 Unit_T operator*(const int64_t scalar) const {
275 return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
276 }
277 Unit_T operator*(const int32_t scalar) const {
278 return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
279 }
280
281 protected:
282 using UnitBase<Unit_T>::UnitBase;
283};
284
285template <class Unit_T>
286inline Unit_T operator*(const double scalar, const RelativeUnit<Unit_T> other) {
287 return other * scalar;
288}
289template <class Unit_T>
290inline Unit_T operator*(const int64_t scalar,
291 const RelativeUnit<Unit_T> other) {
292 return other * scalar;
293}
294template <class Unit_T>
295inline Unit_T operator*(const int32_t& scalar,
296 const RelativeUnit<Unit_T> other) {
297 return other * scalar;
298}
299
300} // namespace rtc_units_impl
301
302} // namespace webrtc
303
304#endif // RTC_BASE_UNITS_UNIT_BASE_H_