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