1 /*
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 
15 #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 
23 namespace webrtc {
24 namespace rtc_units_impl {
25 
26 // UnitBase is a base class for implementing custom value types with a specific
27 // unit. It provides type safety and commonly useful operations. The underlying
28 // 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.
35 template <class Unit_T>
36 class UnitBase {
37  public:
38   UnitBase() = delete;
Zero()39   static constexpr Unit_T Zero() { return Unit_T(0); }
PlusInfinity()40   static constexpr Unit_T PlusInfinity() { return Unit_T(PlusInfinityVal()); }
MinusInfinity()41   static constexpr Unit_T MinusInfinity() { return Unit_T(MinusInfinityVal()); }
42 
IsZero()43   constexpr bool IsZero() const { return value_ == 0; }
IsFinite()44   constexpr bool IsFinite() const { return !IsInfinite(); }
IsInfinite()45   constexpr bool IsInfinite() const {
46     return value_ == PlusInfinityVal() || value_ == MinusInfinityVal();
47   }
IsPlusInfinity()48   constexpr bool IsPlusInfinity() const { return value_ == PlusInfinityVal(); }
IsMinusInfinity()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   }
RoundTo(const Unit_T & resolution)71   constexpr 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   }
RoundUpTo(const Unit_T & resolution)78   constexpr 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   }
RoundDownTo(const Unit_T & resolution)85   constexpr 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   }
91 
92  protected:
93   template <
94       typename T,
95       typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
FromValue(T value)96   static constexpr Unit_T FromValue(T value) {
97     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>
FromValue(T value)106   static constexpr Unit_T FromValue(T value) {
107     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 <
118       typename T,
119       typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
FromFraction(int64_t denominator,T value)120   static constexpr Unit_T FromFraction(int64_t denominator, T value) {
121     if (Unit_T::one_sided)
122       RTC_DCHECK_GE(value, 0);
123     RTC_DCHECK_GT(value, MinusInfinityVal() / denominator);
124     RTC_DCHECK_LT(value, PlusInfinityVal() / denominator);
125     return Unit_T(rtc::dchecked_cast<int64_t>(value * denominator));
126   }
127   template <typename T,
128             typename std::enable_if<std::is_floating_point<T>::value>::type* =
129                 nullptr>
FromFraction(int64_t denominator,T value)130   static constexpr Unit_T FromFraction(int64_t denominator, T value) {
131     return FromValue(value * denominator);
132   }
133 
134   template <typename T = int64_t>
135   constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
ToValue()136   ToValue() const {
137     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
ToValue()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>
ToValueOr(T fallback_value)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>
154   constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
ToFraction()155   ToFraction() const {
156     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
ToFraction()166   ToFraction() const {
167     return ToValue<T>() * (1 / static_cast<T>(Denominator));
168   }
169 
170   template <int64_t Denominator>
ToFractionOr(int64_t fallback_value)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>
179   constexpr typename std::enable_if<std::is_integral<T>::value, T>::type
ToMultiple()180   ToMultiple() const {
181     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
ToMultiple()187   ToMultiple() const {
188     return ToValue<T>() * Factor;
189   }
190 
UnitBase(int64_t value)191   explicit constexpr UnitBase(int64_t value) : value_(value) {}
192 
193  private:
194   template <class RelativeUnit_T>
195   friend class RelativeUnit;
196 
PlusInfinityVal()197   static inline constexpr int64_t PlusInfinityVal() {
198     return std::numeric_limits<int64_t>::max();
199   }
MinusInfinityVal()200   static inline constexpr int64_t MinusInfinityVal() {
201     return std::numeric_limits<int64_t>::min();
202   }
203 
AsSubClassRef()204   constexpr Unit_T& AsSubClassRef() { return static_cast<Unit_T&>(*this); }
AsSubClassRef()205   constexpr const Unit_T& AsSubClassRef() const {
206     return static_cast<const Unit_T&>(*this);
207   }
208   // Assumes that n >= 0 and d > 0.
DivRoundPositiveToNearest(int64_t n,int64_t d)209   static constexpr int64_t DivRoundPositiveToNearest(int64_t n, int64_t d) {
210     return (n + d / 2) / d;
211   }
212   // Assumes that d > 0.
DivRoundToNearest(int64_t n,int64_t d)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.
223 template <class Unit_T>
224 class RelativeUnit : public UnitBase<Unit_T> {
225  public:
Clamped(Unit_T min_value,Unit_T max_value)226   constexpr Unit_T Clamped(Unit_T min_value, Unit_T max_value) const {
227     return std::max(min_value,
228                     std::min(UnitBase<Unit_T>::AsSubClassRef(), max_value));
229   }
Clamp(Unit_T min_value,Unit_T max_value)230   constexpr void Clamp(Unit_T min_value, Unit_T max_value) {
231     *this = Clamped(min_value, max_value);
232   }
233   constexpr Unit_T operator+(const Unit_T other) const {
234     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   }
245   constexpr Unit_T operator-(const Unit_T other) const {
246     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   }
257   constexpr Unit_T& operator+=(const Unit_T other) {
258     *this = *this + other;
259     return this->AsSubClassRef();
260   }
261   constexpr Unit_T& operator-=(const Unit_T other) {
262     *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>
270   constexpr typename std::enable_if<std::is_arithmetic<T>::value, Unit_T>::type
271   operator/(const T& scalar) const {
272     return UnitBase<Unit_T>::FromValue(
273         std::round(UnitBase<Unit_T>::template ToValue<int64_t>() / scalar));
274   }
275   constexpr Unit_T operator*(double scalar) const {
276     return UnitBase<Unit_T>::FromValue(std::round(this->ToValue() * scalar));
277   }
278   constexpr Unit_T operator*(int64_t scalar) const {
279     return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
280   }
281   constexpr Unit_T operator*(int32_t scalar) const {
282     return UnitBase<Unit_T>::FromValue(this->ToValue() * scalar);
283   }
284 
285  protected:
286   using UnitBase<Unit_T>::UnitBase;
287 };
288 
289 template <class Unit_T>
290 inline constexpr Unit_T operator*(double scalar, RelativeUnit<Unit_T> other) {
291   return other * scalar;
292 }
293 template <class Unit_T>
294 inline constexpr Unit_T operator*(int64_t scalar, RelativeUnit<Unit_T> other) {
295   return other * scalar;
296 }
297 template <class Unit_T>
298 inline constexpr Unit_T operator*(int32_t scalar, RelativeUnit<Unit_T> other) {
299   return other * scalar;
300 }
301 
302 }  // namespace rtc_units_impl
303 
304 }  // namespace webrtc
305 
306 #endif  // RTC_BASE_UNITS_UNIT_BASE_H_
307