Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 5 #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
| 6 #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 6 #define BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
| 7 | 7 |
| 8 #include <stddef.h> | 8 #include <stddef.h> |
| 9 #include <stdint.h> | 9 #include <stdint.h> |
| 10 | 10 |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 175 (IntegerBitsPlusSign<__typeof__(x * y)>::value == | 175 (IntegerBitsPlusSign<__typeof__(x * y)>::value == |
| 176 IntegerBitsPlusSign<intptr_t>::value && | 176 IntegerBitsPlusSign<intptr_t>::value && |
| 177 std::is_signed<T>::value == std::is_signed<U>::value); | 177 std::is_signed<T>::value == std::is_signed<U>::value); |
| 178 #else | 178 #else |
| 179 static const bool kUseMaxInt = true; | 179 static const bool kUseMaxInt = true; |
| 180 #endif | 180 #endif |
| 181 if (kUseMaxInt) | 181 if (kUseMaxInt) |
| 182 return !__builtin_mul_overflow(x, y, result); | 182 return !__builtin_mul_overflow(x, y, result); |
| 183 #endif | 183 #endif |
| 184 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; | 184 using Promotion = typename FastIntegerArithmeticPromotion<T, U>::type; |
| 185 // Verify the destination type can hold the result (always true for 0). | |
| 186 if (!(IsValueInRangeForNumericType<Promotion>(x) && | |
| 187 IsValueInRangeForNumericType<Promotion>(y)) && | |
| 188 x && y) { | |
| 189 return false; | |
|
jschuh
2017/06/26 20:26:50
The only functional change here is supporting the
| |
| 190 } | |
| 185 Promotion presult; | 191 Promotion presult; |
| 186 // Fail if either operand is out of range for the promoted type. | 192 bool is_valid = true; |
| 187 // TODO(jschuh): This could be made to work for a broader range of values. | |
| 188 bool is_valid = IsValueInRangeForNumericType<Promotion>(x) && | |
| 189 IsValueInRangeForNumericType<Promotion>(y); | |
| 190 | |
| 191 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { | 193 if (IsIntegerArithmeticSafe<Promotion, T, U>::value) { |
| 192 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); | 194 presult = static_cast<Promotion>(x) * static_cast<Promotion>(y); |
| 193 } else { | 195 } else { |
| 194 is_valid &= CheckedMulImpl(static_cast<Promotion>(x), | 196 is_valid = CheckedMulImpl(static_cast<Promotion>(x), |
| 195 static_cast<Promotion>(y), &presult); | 197 static_cast<Promotion>(y), &presult); |
| 196 } | 198 } |
| 197 *result = static_cast<V>(presult); | 199 *result = static_cast<V>(presult); |
| 198 return is_valid && IsValueInRangeForNumericType<V>(presult); | 200 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 199 } | 201 } |
| 200 }; | 202 }; |
| 201 | 203 |
| 202 // Avoid poluting the namespace once we're done with the macro. | 204 // Avoid poluting the namespace once we're done with the macro. |
| 203 #undef USE_OVERFLOW_BUILTINS | 205 #undef USE_OVERFLOW_BUILTINS |
| 204 | 206 |
| 205 // Division just requires a check for a zero denominator or an invalid negation | 207 // Division just requires a check for a zero denominator or an invalid negation |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 235 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), | 237 is_valid &= CheckedDivImpl(static_cast<Promotion>(x), |
| 236 static_cast<Promotion>(y), &presult); | 238 static_cast<Promotion>(y), &presult); |
| 237 *result = static_cast<V>(presult); | 239 *result = static_cast<V>(presult); |
| 238 return is_valid && IsValueInRangeForNumericType<V>(presult); | 240 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 239 } | 241 } |
| 240 }; | 242 }; |
| 241 | 243 |
| 242 template <typename T> | 244 template <typename T> |
| 243 bool CheckedModImpl(T x, T y, T* result) { | 245 bool CheckedModImpl(T x, T y, T* result) { |
| 244 static_assert(std::is_integral<T>::value, "Type must be integral"); | 246 static_assert(std::is_integral<T>::value, "Type must be integral"); |
| 245 if (y > 0) { | 247 if (y) { |
| 246 *result = static_cast<T>(x % y); | 248 *result = static_cast<T>(x % y); |
| 247 return true; | 249 return true; |
| 248 } | 250 } |
| 249 return false; | 251 return false; |
| 250 } | 252 } |
| 251 | 253 |
| 252 template <typename T, typename U, class Enable = void> | 254 template <typename T, typename U, class Enable = void> |
| 253 struct CheckedModOp {}; | 255 struct CheckedModOp {}; |
| 254 | 256 |
| 255 template <typename T, typename U> | 257 template <typename T, typename U> |
| 256 struct CheckedModOp<T, | 258 struct CheckedModOp<T, |
| 257 U, | 259 U, |
| 258 typename std::enable_if<std::is_integral<T>::value && | 260 typename std::enable_if<std::is_integral<T>::value && |
| 259 std::is_integral<U>::value>::type> { | 261 std::is_integral<U>::value>::type> { |
| 260 using result_type = typename MaxExponentPromotion<T, U>::type; | 262 using result_type = typename MaxExponentPromotion<T, U>::type; |
| 261 template <typename V> | 263 template <typename V> |
| 262 static bool Do(T x, U y, V* result) { | 264 static bool Do(T x, U y, V* result) { |
| 263 using Promotion = typename BigEnoughPromotion<T, U>::type; | 265 using Promotion = typename BigEnoughPromotion<T, U>::type; |
| 264 Promotion presult; | 266 Promotion presult; |
| 265 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), | 267 bool is_valid = CheckedModImpl(static_cast<Promotion>(x), |
| 266 static_cast<Promotion>(y), &presult); | 268 static_cast<Promotion>(y), &presult); |
| 267 *result = static_cast<V>(presult); | 269 *result = static_cast<V>(presult); |
| 268 return is_valid && IsValueInRangeForNumericType<V>(presult); | 270 return is_valid && IsValueInRangeForNumericType<V>(presult); |
| 269 } | 271 } |
| 270 }; | 272 }; |
| 271 | 273 |
| 272 template <typename T, typename U, class Enable = void> | 274 template <typename T, typename U, class Enable = void> |
| 273 struct CheckedLshOp {}; | 275 struct CheckedLshOp {}; |
| 274 | 276 |
| 275 // Left shift. Shifts less than 0 or greater than or equal to the number | |
| 276 // of bits in the promoted type are undefined. Shifts of negative values | |
| 277 // are undefined. Otherwise it is defined when the result fits. | |
| 278 template <typename T, typename U> | 277 template <typename T, typename U> |
| 279 struct CheckedLshOp<T, | 278 struct CheckedLshOp<T, |
| 280 U, | 279 U, |
| 281 typename std::enable_if<std::is_integral<T>::value && | 280 typename std::enable_if<std::is_integral<T>::value && |
| 282 std::is_integral<U>::value>::type> { | 281 std::is_integral<U>::value>::type> { |
| 283 using result_type = T; | 282 using result_type = T; |
| 284 template <typename V> | 283 template <typename V> |
| 285 static bool Do(T x, U shift, V* result) { | 284 static bool Do(T x, U shift, V* result) { |
| 286 using ShiftType = typename std::make_unsigned<T>::type; | 285 if (!IsValueNegative(x) && |
| 287 static const ShiftType kBitWidth = IntegerBitsPlusSign<T>::value; | 286 as_unsigned(shift) < std::numeric_limits<T>::digits) { |
| 288 const ShiftType real_shift = static_cast<ShiftType>(shift); | |
| 289 // Signed shift is not legal on negative values. | |
| 290 if (!IsValueNegative(x) && real_shift < kBitWidth) { | |
| 291 // Just use a multiplication because it's easy. | 287 // Just use a multiplication because it's easy. |
| 292 // TODO(jschuh): This could probably be made more efficient. | 288 // TODO(jschuh): This could probably be made more efficient. |
| 293 if (!std::is_signed<T>::value || real_shift != kBitWidth - 1) | 289 return CheckedMulOp<T, T>::Do(x, T(1) << shift, result); |
| 294 return CheckedMulOp<T, T>::Do(x, static_cast<T>(1) << shift, result); | |
| 295 return !x; // Special case zero for a full width signed shift. | |
| 296 } | 290 } |
| 297 return false; | 291 // The standard defines only the result of the shift, rather than the |
| 292 // values of source operands. So, shifting zero by any amount is legal. | |
| 293 // Otherwise, we overflowed or have an undefined operation. | |
| 294 return !x ? (*result = V(0)), true : false; | |
|
jschuh
2017/06/26 20:26:50
Stumbled on a spec error in how I was handling zer
dcheng
2017/06/28 07:25:06
Acknowledged--though for future CLs, it would be n
jschuh
2017/06/28 12:32:37
Weird. I'm looking at it now and you're right. May
| |
| 298 } | 295 } |
| 299 }; | 296 }; |
| 300 | 297 |
| 301 template <typename T, typename U, class Enable = void> | 298 template <typename T, typename U, class Enable = void> |
| 302 struct CheckedRshOp {}; | 299 struct CheckedRshOp {}; |
| 303 | 300 |
| 304 // Right shift. Shifts less than 0 or greater than or equal to the number | |
| 305 // of bits in the promoted type are undefined. Otherwise, it is always defined, | |
| 306 // but a right shift of a negative value is implementation-dependent. | |
| 307 template <typename T, typename U> | 301 template <typename T, typename U> |
| 308 struct CheckedRshOp<T, | 302 struct CheckedRshOp<T, |
| 309 U, | 303 U, |
| 310 typename std::enable_if<std::is_integral<T>::value && | 304 typename std::enable_if<std::is_integral<T>::value && |
| 311 std::is_integral<U>::value>::type> { | 305 std::is_integral<U>::value>::type> { |
| 312 using result_type = T; | 306 using result_type = T; |
| 313 template <typename V> | 307 template <typename V> |
| 314 static bool Do(T x, U shift, V* result) { | 308 static bool Do(T x, U shift, V* result) { |
| 315 // Use the type conversion push negative values out of range. | 309 // Use the type conversion push negative values out of range. |
| 316 using ShiftType = typename std::make_unsigned<T>::type; | 310 using ShiftType = typename std::make_unsigned<T>::type; |
| 317 if (static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { | 311 if (!x || static_cast<ShiftType>(shift) < IntegerBitsPlusSign<T>::value) { |
|
jschuh
2017/06/26 20:26:50
Stumbled on a spec error in how I was handling zer
dcheng
2017/06/28 07:25:06
Ditto.
jschuh
2017/06/28 12:32:37
Yup.
| |
| 318 T tmp = x >> shift; | 312 T tmp = x >> shift; |
| 319 *result = static_cast<V>(tmp); | 313 *result = static_cast<V>(tmp); |
| 320 return IsValueInRangeForNumericType<V>(tmp); | 314 return IsValueInRangeForNumericType<V>(tmp); |
| 321 } | 315 } |
| 322 return false; | 316 return false; |
| 323 } | 317 } |
| 324 }; | 318 }; |
| 325 | 319 |
| 326 template <typename T, typename U, class Enable = void> | 320 template <typename T, typename U, class Enable = void> |
| 327 struct CheckedAndOp {}; | 321 struct CheckedAndOp {}; |
| (...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 564 return value_ <= std::numeric_limits<T>::max() && | 558 return value_ <= std::numeric_limits<T>::max() && |
| 565 value_ >= std::numeric_limits<T>::lowest(); | 559 value_ >= std::numeric_limits<T>::lowest(); |
| 566 } | 560 } |
| 567 constexpr T value() const { return value_; } | 561 constexpr T value() const { return value_; } |
| 568 }; | 562 }; |
| 569 | 563 |
| 570 } // namespace internal | 564 } // namespace internal |
| 571 } // namespace base | 565 } // namespace base |
| 572 | 566 |
| 573 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 567 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ |
| OLD | NEW |