blob: 533d35583dfb1a4e851bcd9d54e53b9fb57df05a [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 Jansson26b5e352019-06-07 11:05:31 +020071 Unit_T RoundTo(const Unit_T& resolution) const {
72 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 }
78 Unit_T RoundUpTo(const Unit_T& resolution) const {
79 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 }
85 Unit_T RoundDownTo(const Unit_T& resolution) const {
86 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:
93 template <int64_t value>
94 static constexpr Unit_T FromStaticValue() {
95 static_assert(value >= 0 || !Unit_T::one_sided, "");
96 static_assert(value > MinusInfinityVal(), "");
97 static_assert(value < PlusInfinityVal(), "");
98 return Unit_T(value);
99 }
100
101 template <int64_t fraction_value, int64_t Denominator>
102 static constexpr Unit_T FromStaticFraction() {
103 static_assert(fraction_value >= 0 || !Unit_T::one_sided, "");
104 static_assert(fraction_value > MinusInfinityVal() / Denominator, "");
105 static_assert(fraction_value < PlusInfinityVal() / Denominator, "");
106 return Unit_T(fraction_value * Denominator);
107 }
108
109 template <
110 typename T,
111 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
112 static Unit_T FromValue(T value) {
113 if (Unit_T::one_sided)
114 RTC_DCHECK_GE(value, 0);
115 RTC_DCHECK_GT(value, MinusInfinityVal());
116 RTC_DCHECK_LT(value, PlusInfinityVal());
117 return Unit_T(rtc::dchecked_cast<int64_t>(value));
118 }
119 template <typename T,
120 typename std::enable_if<std::is_floating_point<T>::value>::type* =
121 nullptr>
122 static Unit_T FromValue(T value) {
123 if (value == std::numeric_limits<T>::infinity()) {
124 return PlusInfinity();
125 } else if (value == -std::numeric_limits<T>::infinity()) {
126 return MinusInfinity();
127 } else {
128 RTC_DCHECK(!std::isnan(value));
129 return FromValue(rtc::dchecked_cast<int64_t>(value));
130 }
131 }
132
133 template <
134 int64_t Denominator,
135 typename T,
136 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
137 static Unit_T FromFraction(T value) {
138 if (Unit_T::one_sided)
139 RTC_DCHECK_GE(value, 0);
140 RTC_DCHECK_GT(value, MinusInfinityVal() / Denominator);
141 RTC_DCHECK_LT(value, PlusInfinityVal() / Denominator);
142 return Unit_T(rtc::dchecked_cast<int64_t>(value * Denominator));
143 }
144 template <int64_t Denominator,
145 typename T,
146 typename std::enable_if<std::is_floating_point<T>::value>::type* =
147 nullptr>
148 static Unit_T FromFraction(T value) {
149 return FromValue(value * Denominator);
150 }
151
152 template <typename T = int64_t>
153 typename std::enable_if<std::is_integral<T>::value, T>::type ToValue() const {
154 RTC_DCHECK(IsFinite());
155 return rtc::dchecked_cast<T>(value_);
156 }
157 template <typename T>
158 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
159 ToValue() const {
160 return IsPlusInfinity()
161 ? std::numeric_limits<T>::infinity()
162 : IsMinusInfinity() ? -std::numeric_limits<T>::infinity()
163 : value_;
164 }
165 template <typename T>
166 constexpr T ToValueOr(T fallback_value) const {
167 return IsFinite() ? value_ : fallback_value;
168 }
169
170 template <int64_t Denominator, typename T = int64_t>
171 typename std::enable_if<std::is_integral<T>::value, T>::type ToFraction()
172 const {
173 RTC_DCHECK(IsFinite());
174 if (Unit_T::one_sided) {
175 return rtc::dchecked_cast<T>(
176 DivRoundPositiveToNearest(value_, Denominator));
177 } else {
178 return rtc::dchecked_cast<T>(DivRoundToNearest(value_, Denominator));
179 }
180 }
181 template <int64_t Denominator, typename T>
182 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
183 ToFraction() const {
184 return ToValue<T>() * (1 / static_cast<T>(Denominator));
185 }
186
187 template <int64_t Denominator>
188 constexpr int64_t ToFractionOr(int64_t fallback_value) const {
189 return IsFinite() ? Unit_T::one_sided
190 ? DivRoundPositiveToNearest(value_, Denominator)
191 : DivRoundToNearest(value_, Denominator)
192 : fallback_value;
193 }
194
195 template <int64_t Factor, typename T = int64_t>
196 typename std::enable_if<std::is_integral<T>::value, T>::type ToMultiple()
197 const {
198 RTC_DCHECK_GE(ToValue(), std::numeric_limits<T>::min() / Factor);
199 RTC_DCHECK_LE(ToValue(), std::numeric_limits<T>::max() / Factor);
200 return rtc::dchecked_cast<T>(ToValue() * Factor);
201 }
202 template <int64_t Factor, typename T>
203 constexpr typename std::enable_if<std::is_floating_point<T>::value, T>::type
204 ToMultiple() const {
205 return ToValue<T>() * Factor;
206 }
207
208 explicit constexpr UnitBase(int64_t value) : value_(value) {}
209
210 private:
211 template <class RelativeUnit_T>
212 friend class RelativeUnit;
213
214 static inline constexpr int64_t PlusInfinityVal() {
215 return std::numeric_limits<int64_t>::max();
216 }
217 static inline constexpr int64_t MinusInfinityVal() {
218 return std::numeric_limits<int64_t>::min();
219 }
220
221 Unit_T& AsSubClassRef() { return reinterpret_cast<Unit_T&>(*this); }
222 constexpr const Unit_T& AsSubClassRef() const {
223 return reinterpret_cast<const Unit_T&>(*this);
224 }
225 // Assumes that n >= 0 and d > 0.
226 static constexpr int64_t DivRoundPositiveToNearest(int64_t n, int64_t d) {
227 return (n + d / 2) / d;
228 }
229 // Assumes that d > 0.
230 static constexpr int64_t DivRoundToNearest(int64_t n, int64_t d) {
231 return (n + (n >= 0 ? d / 2 : -d / 2)) / d;
232 }
233
234 int64_t value_;
235};
236
237// Extends UnitBase to provide operations for relative units, that is, units
238// that have a meaningful relation between values such that a += b is a
239// sensible thing to do. For a,b <- same unit.
240template <class Unit_T>
241class RelativeUnit : public UnitBase<Unit_T> {
242 public:
243 Unit_T Clamped(Unit_T min_value, Unit_T max_value) const {
244 return std::max(min_value,
245 std::min(UnitBase<Unit_T>::AsSubClassRef(), max_value));
246 }
247 void Clamp(Unit_T min_value, Unit_T max_value) {
248 *this = Clamped(min_value, max_value);
249 }
250 Unit_T operator+(const Unit_T other) const {
251 if (this->IsPlusInfinity() || other.IsPlusInfinity()) {
252 RTC_DCHECK(!this->IsMinusInfinity());
253 RTC_DCHECK(!other.IsMinusInfinity());
254 return this->PlusInfinity();
255 } else if (this->IsMinusInfinity() || other.IsMinusInfinity()) {
256 RTC_DCHECK(!this->IsPlusInfinity());
257 RTC_DCHECK(!other.IsPlusInfinity());
258 return this->MinusInfinity();
259 }
260 return UnitBase<Unit_T>::FromValue(this->ToValue() + other.ToValue());
261 }
262 Unit_T operator-(const Unit_T other) const {
263 if (this->IsPlusInfinity() || other.IsMinusInfinity()) {
264 RTC_DCHECK(!this->IsMinusInfinity());
265 RTC_DCHECK(!other.IsPlusInfinity());
266 return this->PlusInfinity();
267 } else if (this->IsMinusInfinity() || other.IsPlusInfinity()) {
268 RTC_DCHECK(!this->IsPlusInfinity());
269 RTC_DCHECK(!other.IsMinusInfinity());
270 return this->MinusInfinity();
271 }
272 return UnitBase<Unit_T>::FromValue(this->ToValue() - other.ToValue());
273 }
274 Unit_T& operator+=(const Unit_T other) {
275 *this = *this + other;
276 return this->AsSubClassRef();
277 }
278 Unit_T& operator-=(const Unit_T other) {
279 *this = *this - other;
280 return this->AsSubClassRef();
281 }
282 constexpr double operator/(const Unit_T other) const {
283 return UnitBase<Unit_T>::template ToValue<double>() /
284 other.template ToValue<double>();
285 }
286 template <typename T>
287 typename std::enable_if<std::is_arithmetic<T>::value, Unit_T>::type operator/(
288 const T& scalar) const {
289 return UnitBase<Unit_T>::FromValue(
290 std::round(UnitBase<Unit_T>::template ToValue<int64_t>() / scalar));
291 }
292 Unit_T operator*(const double scalar) const {
293 return UnitBase<Unit_T>::FromValue(std::round(this->ToValue() * scalar));
294 }
295 Unit_T operator*(const int64_t scalar) const {
296 return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
297 }
298 Unit_T operator*(const int32_t scalar) const {
299 return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
300 }
301
302 protected:
303 using UnitBase<Unit_T>::UnitBase;
304};
305
306template <class Unit_T>
307inline Unit_T operator*(const double scalar, const RelativeUnit<Unit_T> other) {
308 return other * scalar;
309}
310template <class Unit_T>
311inline Unit_T operator*(const int64_t scalar,
312 const RelativeUnit<Unit_T> other) {
313 return other * scalar;
314}
315template <class Unit_T>
316inline Unit_T operator*(const int32_t& scalar,
317 const RelativeUnit<Unit_T> other) {
318 return other * scalar;
319}
320
321} // namespace rtc_units_impl
322
323} // namespace webrtc
324
325#endif // RTC_BASE_UNITS_UNIT_BASE_H_