Saturate.h (6197B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* Provides saturation arithmetics for scalar types. */ 8 9 #ifndef mozilla_Saturate_h 10 #define mozilla_Saturate_h 11 12 #include <limits> 13 #include <stdint.h> 14 #include <type_traits> 15 #include <utility> 16 17 #include "mozilla/Attributes.h" 18 #include "mozilla/CheckedArithmetic.h" 19 20 namespace mozilla { 21 namespace detail { 22 23 /** 24 * |SaturateOp<T>| wraps scalar values for saturation arithmetics. Usage: 25 * 26 * uint32_t value = 1; 27 * 28 * ++SaturateOp<uint32_t>(value); // value is 2 29 * --SaturateOp<uint32_t>(value); // value is 1 30 * --SaturateOp<uint32_t>(value); // value is 0 31 * --SaturateOp<uint32_t>(value); // value is still 0 32 * 33 * Please add new operators when required. 34 * 35 * |SaturateOp<T>| will saturate at the minimum and maximum values of 36 * type T. If you need other bounds, implement a clamped-type class and 37 * specialize the type traits accordingly. 38 */ 39 template <typename T> 40 class SaturateOp { 41 public: 42 explicit SaturateOp(T& aValue) : mValue(aValue) { 43 // We should actually check for |std::is_scalar<T>::value| to be 44 // true, but this type trait is not available everywhere. Relax 45 // this assertion if you want to use floating point values as well. 46 static_assert(std::is_integral_v<T>, 47 "Integral type required in instantiation"); 48 } 49 50 // Add and subtract operators 51 52 T operator+(const T& aRhs) const { return T(mValue) += aRhs; } 53 54 T operator-(const T& aRhs) const { return T(mValue) -= aRhs; } 55 56 // Compound operators 57 const T& operator+=(const T& aRhs) const { 58 constexpr T min = std::numeric_limits<T>::min(); 59 constexpr T max = std::numeric_limits<T>::max(); 60 if (!mozilla::SafeAdd(mValue, aRhs, &mValue)) { 61 return mValue = (aRhs > 0 ? max : min); 62 } 63 return mValue; 64 } 65 66 const T& operator-=(const T& aRhs) const { 67 constexpr T min = std::numeric_limits<T>::min(); 68 constexpr T max = std::numeric_limits<T>::max(); 69 if (!mozilla::SafeSub(mValue, aRhs, &mValue)) { 70 return mValue = (aRhs > 0 ? min : max); 71 } 72 return mValue; 73 } 74 75 // Increment and decrement operators 76 77 const T& operator++() const // prefix 78 { 79 return operator+=(static_cast<T>(1)); 80 } 81 82 T operator++(int) const // postfix 83 { 84 const T value(mValue); 85 operator++(); 86 return value; 87 } 88 89 const T& operator--() const // prefix 90 { 91 return operator-=(static_cast<T>(1)); 92 } 93 94 T operator--(int) const // postfix 95 { 96 const T value(mValue); 97 operator--(); 98 return value; 99 } 100 101 private: 102 SaturateOp(const SaturateOp<T>&) = delete; 103 SaturateOp(SaturateOp<T>&&) = delete; 104 SaturateOp& operator=(const SaturateOp<T>&) = delete; 105 SaturateOp& operator=(SaturateOp<T>&&) = delete; 106 107 T& mValue; 108 }; 109 110 /** 111 * |Saturate<T>| is a value type for saturation arithmetics. It's 112 * built on top of |SaturateOp<T>|. 113 */ 114 template <typename T> 115 class Saturate { 116 public: 117 Saturate() = default; 118 MOZ_IMPLICIT Saturate(const Saturate<T>&) = default; 119 120 MOZ_IMPLICIT Saturate(Saturate<T>&& aValue) { 121 mValue = std::move(aValue.mValue); 122 } 123 124 explicit Saturate(const T& aValue) : mValue(aValue) {} 125 126 const T& value() const { return mValue; } 127 128 // Compare operators 129 130 bool operator==(const Saturate<T>& aRhs) const { 131 return mValue == aRhs.mValue; 132 } 133 134 bool operator!=(const Saturate<T>& aRhs) const { return !operator==(aRhs); } 135 136 bool operator==(const T& aRhs) const { return mValue == aRhs; } 137 138 bool operator!=(const T& aRhs) const { return !operator==(aRhs); } 139 140 // Assignment operators 141 142 Saturate<T>& operator=(const Saturate<T>&) = default; 143 144 Saturate<T>& operator=(Saturate<T>&& aRhs) { 145 mValue = std::move(aRhs.mValue); 146 return *this; 147 } 148 149 // Add and subtract operators 150 151 Saturate<T> operator+(const Saturate<T>& aRhs) const { 152 Saturate<T> lhs(mValue); 153 return lhs += aRhs.mValue; 154 } 155 156 Saturate<T> operator+(const T& aRhs) const { 157 Saturate<T> lhs(mValue); 158 return lhs += aRhs; 159 } 160 161 Saturate<T> operator-(const Saturate<T>& aRhs) const { 162 Saturate<T> lhs(mValue); 163 return lhs -= aRhs.mValue; 164 } 165 166 Saturate<T> operator-(const T& aRhs) const { 167 Saturate<T> lhs(mValue); 168 return lhs -= aRhs; 169 } 170 171 // Compound operators 172 173 Saturate<T>& operator+=(const Saturate<T>& aRhs) { 174 SaturateOp<T>(mValue) += aRhs.mValue; 175 return *this; 176 } 177 178 Saturate<T>& operator+=(const T& aRhs) { 179 SaturateOp<T>(mValue) += aRhs; 180 return *this; 181 } 182 183 Saturate<T>& operator-=(const Saturate<T>& aRhs) { 184 SaturateOp<T>(mValue) -= aRhs.mValue; 185 return *this; 186 } 187 188 Saturate<T>& operator-=(const T& aRhs) { 189 SaturateOp<T>(mValue) -= aRhs; 190 return *this; 191 } 192 193 // Increment and decrement operators 194 195 Saturate<T>& operator++() // prefix 196 { 197 ++SaturateOp<T>(mValue); 198 return *this; 199 } 200 201 Saturate<T> operator++(int) // postfix 202 { 203 return Saturate<T>(SaturateOp<T>(mValue)++); 204 } 205 206 Saturate<T>& operator--() // prefix 207 { 208 --SaturateOp<T>(mValue); 209 return *this; 210 } 211 212 Saturate<T> operator--(int) // postfix 213 { 214 return Saturate<T>(SaturateOp<T>(mValue)--); 215 } 216 217 private: 218 T mValue; 219 }; 220 221 } // namespace detail 222 223 typedef detail::Saturate<int8_t> SaturateInt8; 224 typedef detail::Saturate<int16_t> SaturateInt16; 225 typedef detail::Saturate<int32_t> SaturateInt32; 226 typedef detail::Saturate<uint8_t> SaturateUint8; 227 typedef detail::Saturate<uint16_t> SaturateUint16; 228 typedef detail::Saturate<uint32_t> SaturateUint32; 229 typedef detail::Saturate<intptr_t> SaturateIntPtr; 230 typedef detail::Saturate<uintptr_t> SaturateUintPtr; 231 232 using detail::Saturate; 233 234 } // namespace mozilla 235 236 template <typename LhsT, typename RhsT> 237 bool operator==(LhsT aLhs, const mozilla::detail::Saturate<RhsT>& aRhs) { 238 return aRhs.operator==(static_cast<RhsT>(aLhs)); 239 } 240 241 template <typename LhsT, typename RhsT> 242 bool operator!=(LhsT aLhs, const mozilla::detail::Saturate<RhsT>& aRhs) { 243 return !(aLhs == aRhs); 244 } 245 246 #endif // mozilla_Saturate_h