Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #ifndef BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ | |
| 6 #define BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ | |
| 7 | |
| 8 #include <stddef.h> | |
| 9 #include <stdint.h> | |
| 10 | |
| 11 #include <climits> | |
| 12 #include <cmath> | |
| 13 #include <cstdlib> | |
| 14 #include <limits> | |
| 15 #include <type_traits> | |
| 16 | |
| 17 #include "base/numerics/checked_math.h" | |
| 18 #include "base/numerics/safe_conversions.h" | |
| 19 #include "base/numerics/safe_math_shared_impl.h" | |
| 20 | |
| 21 namespace base { | |
| 22 namespace internal { | |
| 23 | |
| 24 // This provides a small optimization that generates more compact code when one | |
| 25 // of the components in an operation is a compile-time constant. | |
| 26 template <typename T> | |
| 27 constexpr bool IsValueConstantAndNegative(const T v) { | |
| 28 #if defined(__clang__) || defined(__GNUC__) | |
| 29 return __builtin_constant_p(v) ? IsValueNegative(v) : false; | |
| 30 #else | |
| 31 return false; | |
| 32 #endif | |
| 33 } | |
| 34 | |
| 35 template <typename T, typename U, class Enable = void> | |
| 36 struct ClampedAddOp {}; | |
| 37 | |
| 38 template <typename T, typename U> | |
| 39 struct ClampedAddOp<T, | |
| 40 U, | |
| 41 typename std::enable_if<std::is_integral<T>::value && | |
| 42 std::is_integral<U>::value>::type> { | |
| 43 using result_type = typename MaxExponentPromotion<T, U>::type; | |
| 44 template <typename V = result_type> | |
| 45 static V Do(T x, U y) { | |
| 46 V result; | |
| 47 return CheckedAddOp<T, U>::Do(x, y, &result) | |
| 48 ? result | |
| 49 : (as_unsigned(std::numeric_limits<V>::max()) + | |
| 50 (IsValueConstantAndNegative(x) || IsValueNegative(y))); | |
|
dcheng
2017/06/30 07:39:31
This is glorious and terrible.
P.S. I think these
jschuh
2017/07/01 06:32:00
Done.
| |
| 51 } | |
| 52 }; | |
| 53 | |
| 54 template <typename T, typename U, class Enable = void> | |
| 55 struct ClampedSubOp {}; | |
| 56 | |
| 57 template <typename T, typename U> | |
| 58 struct ClampedSubOp<T, | |
| 59 U, | |
| 60 typename std::enable_if<std::is_integral<T>::value && | |
| 61 std::is_integral<U>::value>::type> { | |
| 62 using result_type = typename MaxExponentPromotion<T, U>::type; | |
| 63 template <typename V = result_type> | |
| 64 static V Do(T x, U y) { | |
| 65 V result; | |
| 66 return CheckedSubOp<T, U>::Do(x, y, &result) | |
| 67 ? result | |
| 68 : (as_unsigned(std::numeric_limits<V>::max()) + | |
| 69 (IsValueConstantAndNegative(x) || !IsValueNegative(y))); | |
|
dcheng
2017/06/30 07:39:31
Ditto.
jschuh
2017/07/01 06:31:59
Done.
| |
| 70 } | |
| 71 }; | |
| 72 | |
| 73 template <typename T, typename U, class Enable = void> | |
| 74 struct ClampedMulOp {}; | |
| 75 | |
| 76 template <typename T, typename U> | |
| 77 struct ClampedMulOp<T, | |
| 78 U, | |
| 79 typename std::enable_if<std::is_integral<T>::value && | |
| 80 std::is_integral<U>::value>::type> { | |
| 81 using result_type = typename MaxExponentPromotion<T, U>::type; | |
| 82 template <typename V = result_type> | |
| 83 static V Do(T x, U y) { | |
| 84 V result; | |
| 85 return CheckedMulOp<T, U>::Do(x, y, &result) | |
| 86 ? result | |
| 87 : as_unsigned(std::numeric_limits<V>::max()) + | |
| 88 (IsValueNegative(x) ^ IsValueNegative(y)); | |
| 89 } | |
| 90 }; | |
| 91 | |
| 92 template <typename T, typename U, class Enable = void> | |
| 93 struct ClampedDivOp {}; | |
| 94 | |
| 95 template <typename T, typename U> | |
| 96 struct ClampedDivOp<T, | |
| 97 U, | |
| 98 typename std::enable_if<std::is_integral<T>::value && | |
| 99 std::is_integral<U>::value>::type> { | |
| 100 using result_type = typename MaxExponentPromotion<T, U>::type; | |
| 101 template <typename V = result_type> | |
| 102 static V Do(T x, U y) { | |
| 103 V result; | |
| 104 return CheckedDivOp<T, U>::Do(x, y, &result) | |
| 105 ? result | |
| 106 : as_unsigned(std::numeric_limits<V>::max()) + | |
| 107 (IsValueNegative(x) ^ IsValueNegative(y)); | |
| 108 } | |
| 109 }; | |
| 110 | |
| 111 template <typename T, typename U, class Enable = void> | |
| 112 struct ClampedModOp {}; | |
| 113 | |
| 114 template <typename T, typename U> | |
| 115 struct ClampedModOp<T, | |
| 116 U, | |
| 117 typename std::enable_if<std::is_integral<T>::value && | |
| 118 std::is_integral<U>::value>::type> { | |
| 119 using result_type = typename MaxExponentPromotion<T, U>::type; | |
| 120 template <typename V = result_type> | |
| 121 static V Do(T x, U y) { | |
| 122 V result; | |
| 123 return CheckedModOp<T, U>::Do(x, y, &result) ? result : x; | |
| 124 } | |
| 125 }; | |
| 126 | |
| 127 template <typename T, typename U, class Enable = void> | |
| 128 struct ClampedLshOp {}; | |
| 129 | |
| 130 // Left shift. Non-zero values saturate in the direction of the sign. A zero | |
| 131 // shifted by any value always results in zero. | |
| 132 // Note: This class template supports left shifting negative values. | |
| 133 template <typename T, typename U> | |
| 134 struct ClampedLshOp<T, | |
| 135 U, | |
| 136 typename std::enable_if<std::is_integral<T>::value && | |
| 137 std::is_integral<U>::value>::type> { | |
| 138 using result_type = T; | |
| 139 template <typename V = result_type> | |
| 140 static V Do(T x, U shift) { | |
| 141 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | |
|
dcheng
2017/06/30 07:39:30
Oh... I guess this static assert is supposed to pr
| |
| 142 V result = x; | |
| 143 return (shift < std::numeric_limits<T>::digits && | |
| 144 CheckedMulOp<T, T>::Do(x, T(1) << shift, &result)) | |
| 145 ? result | |
| 146 : (x ? as_unsigned(std::numeric_limits<V>::max()) + | |
| 147 IsValueNegative(x) | |
| 148 : 0); | |
| 149 } | |
| 150 }; | |
| 151 | |
| 152 template <typename T, typename U, class Enable = void> | |
| 153 struct ClampedRshOp {}; | |
| 154 | |
| 155 // Right shift. Negative values saturate to -1. Positive or 0 saturates to 0. | |
| 156 template <typename T, typename U> | |
| 157 struct ClampedRshOp<T, | |
| 158 U, | |
| 159 typename std::enable_if<std::is_integral<T>::value && | |
| 160 std::is_integral<U>::value>::type> { | |
| 161 using result_type = T; | |
| 162 template <typename V = result_type> | |
| 163 static V Do(T x, U shift) { | |
| 164 static_assert(!std::is_signed<U>::value, "Shift value must be unsigned."); | |
| 165 return shift < IntegerBitsPlusSign<T>::value | |
| 166 ? saturated_cast<V>(x >> shift) | |
| 167 : as_unsigned(V(0)) - IsValueNegative(x); | |
| 168 } | |
| 169 }; | |
| 170 | |
| 171 template <typename T, typename U, class Enable = void> | |
| 172 struct ClampedAndOp {}; | |
| 173 | |
| 174 template <typename T, typename U> | |
| 175 struct ClampedAndOp<T, | |
| 176 U, | |
| 177 typename std::enable_if<std::is_integral<T>::value && | |
| 178 std::is_integral<U>::value>::type> { | |
| 179 using result_type = typename std::make_unsigned< | |
| 180 typename MaxExponentPromotion<T, U>::type>::type; | |
| 181 template <typename V> | |
| 182 static constexpr V Do(T x, U y) { | |
| 183 return static_cast<result_type>(x) & static_cast<result_type>(y); | |
| 184 } | |
| 185 }; | |
| 186 | |
| 187 template <typename T, typename U, class Enable = void> | |
| 188 struct ClampedOrOp {}; | |
| 189 | |
| 190 // For simplicity we promote to unsigned integers. | |
| 191 template <typename T, typename U> | |
| 192 struct ClampedOrOp<T, | |
| 193 U, | |
| 194 typename std::enable_if<std::is_integral<T>::value && | |
| 195 std::is_integral<U>::value>::type> { | |
| 196 using result_type = typename std::make_unsigned< | |
| 197 typename MaxExponentPromotion<T, U>::type>::type; | |
| 198 template <typename V> | |
| 199 static constexpr V Do(T x, U y) { | |
| 200 return static_cast<result_type>(x) | static_cast<result_type>(y); | |
| 201 } | |
| 202 }; | |
| 203 | |
| 204 template <typename T, typename U, class Enable = void> | |
| 205 struct ClampedXorOp {}; | |
| 206 | |
| 207 // For simplicity we support only unsigned integers. | |
| 208 template <typename T, typename U> | |
| 209 struct ClampedXorOp<T, | |
| 210 U, | |
| 211 typename std::enable_if<std::is_integral<T>::value && | |
| 212 std::is_integral<U>::value>::type> { | |
| 213 using result_type = typename std::make_unsigned< | |
| 214 typename MaxExponentPromotion<T, U>::type>::type; | |
| 215 template <typename V> | |
| 216 static constexpr V Do(T x, U y) { | |
| 217 return static_cast<result_type>(x) ^ static_cast<result_type>(y); | |
| 218 } | |
| 219 }; | |
| 220 | |
| 221 template <typename T, typename U, class Enable = void> | |
| 222 struct ClampedMaxOp {}; | |
| 223 | |
| 224 template <typename T, typename U> | |
| 225 struct ClampedMaxOp< | |
| 226 T, | |
| 227 U, | |
| 228 typename std::enable_if<std::is_arithmetic<T>::value && | |
| 229 std::is_arithmetic<U>::value>::type> { | |
| 230 using result_type = typename MaxExponentPromotion<T, U>::type; | |
| 231 template <typename V = result_type> | |
| 232 static constexpr V Do(T x, U y) { | |
| 233 return IsGreater<T, U>::Test(x, y) ? saturated_cast<V>(x) | |
| 234 : saturated_cast<V>(y); | |
| 235 } | |
| 236 }; | |
| 237 | |
| 238 template <typename T, typename U, class Enable = void> | |
| 239 struct ClampedMinOp {}; | |
| 240 | |
| 241 template <typename T, typename U> | |
| 242 struct ClampedMinOp< | |
| 243 T, | |
| 244 U, | |
| 245 typename std::enable_if<std::is_arithmetic<T>::value && | |
| 246 std::is_arithmetic<U>::value>::type> { | |
| 247 using result_type = typename LowestValuePromotion<T, U>::type; | |
| 248 template <typename V = result_type> | |
| 249 static constexpr V Do(T x, U y) { | |
| 250 return IsLess<T, U>::Test(x, y) ? saturated_cast<V>(x) | |
| 251 : saturated_cast<V>(y); | |
| 252 } | |
| 253 }; | |
| 254 | |
| 255 // This is just boilerplate that wraps the standard floating point arithmetic. | |
| 256 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. | |
| 257 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ | |
| 258 template <typename T, typename U> \ | |
| 259 struct Clamped##NAME##Op< \ | |
| 260 T, U, \ | |
| 261 typename std::enable_if<std::is_floating_point<T>::value || \ | |
| 262 std::is_floating_point<U>::value>::type> { \ | |
| 263 using result_type = typename MaxExponentPromotion<T, U>::type; \ | |
| 264 template <typename V = result_type> \ | |
| 265 static constexpr V Do(T x, U y) { \ | |
| 266 return saturated_cast<V>(x OP y); \ | |
| 267 } \ | |
| 268 }; | |
| 269 | |
| 270 BASE_FLOAT_ARITHMETIC_OPS(Add, +) | |
| 271 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) | |
| 272 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) | |
| 273 BASE_FLOAT_ARITHMETIC_OPS(Div, /) | |
| 274 | |
| 275 #undef BASE_FLOAT_ARITHMETIC_OPS | |
| 276 | |
| 277 } // namespace internal | |
| 278 } // namespace base | |
| 279 | |
| 280 #endif // BASE_NUMERICS_CLAMPED_MATH_IMPL_H_ | |
| OLD | NEW |