unit_base.h 11 KB

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