 Chromium Code Reviews
 Chromium Code Reviews Issue 2931323002:
  Split out code to be shared between CheckedNumeric and ClampedNumeric  (Closed)
    
  
    Issue 2931323002:
  Split out code to be shared between CheckedNumeric and ClampedNumeric  (Closed) 
  | OLD | NEW | 
|---|---|
| 1 // Copyright 2014 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_SAFE_MATH_IMPL_H_ | 5 #ifndef BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 
| 6 #define BASE_NUMERICS_SAFE_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 | 
| 11 #include <climits> | 11 #include <climits> | 
| 12 #include <cmath> | 12 #include <cmath> | 
| 13 #include <cstdlib> | 13 #include <cstdlib> | 
| 14 #include <limits> | 14 #include <limits> | 
| 15 #include <type_traits> | 15 #include <type_traits> | 
| 16 | 16 | 
| 17 #include "base/numerics/safe_conversions.h" | 17 #include "base/numerics/safe_conversions.h" | 
| 18 #include "base/numerics/safe_math_shared_impl.h" | |
| 18 | 19 | 
| 19 namespace base { | 20 namespace base { | 
| 20 namespace internal { | 21 namespace internal { | 
| 21 | 22 | 
| 22 // Everything from here up to the floating point operations is portable C++, | |
| 23 // but it may not be fast. This code could be split based on | |
| 24 // platform/architecture and replaced with potentially faster implementations. | |
| 
brucedawson
2017/06/12 21:09:22
This comment now gone from everywhere. Intentional
 
jschuh
2017/06/12 21:19:21
Yup. The move makes the code split clearer, and on
 | |
| 25 | |
| 26 // This is used for UnsignedAbs, where we need to support floating-point | |
| 27 // template instantiations even though we don't actually support the operations. | |
| 28 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, | |
| 29 // so the float versions will not compile. | |
| 30 template <typename Numeric, | |
| 31 bool IsInteger = std::is_integral<Numeric>::value, | |
| 32 bool IsFloat = std::is_floating_point<Numeric>::value> | |
| 33 struct UnsignedOrFloatForSize; | |
| 34 | |
| 35 template <typename Numeric> | |
| 36 struct UnsignedOrFloatForSize<Numeric, true, false> { | |
| 37 using type = typename std::make_unsigned<Numeric>::type; | |
| 38 }; | |
| 39 | |
| 40 template <typename Numeric> | |
| 41 struct UnsignedOrFloatForSize<Numeric, false, true> { | |
| 42 using type = Numeric; | |
| 43 }; | |
| 44 | |
| 45 // Probe for builtin math overflow support on Clang and version check on GCC. | 23 // Probe for builtin math overflow support on Clang and version check on GCC. | 
| 46 #if defined(__has_builtin) | 24 #if defined(__has_builtin) | 
| 47 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) | 25 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) | 
| 48 #elif defined(__GNUC__) | 26 #elif defined(__GNUC__) | 
| 49 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) | 27 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) | 
| 50 #else | 28 #else | 
| 51 #define USE_OVERFLOW_BUILTINS (0) | 29 #define USE_OVERFLOW_BUILTINS (0) | 
| 52 #endif | 30 #endif | 
| 53 | 31 | 
| 54 template <typename T> | 32 template <typename T> | 
| (...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 437 template <typename V = result_type> | 415 template <typename V = result_type> | 
| 438 static bool Do(T x, U y, V* result) { | 416 static bool Do(T x, U y, V* result) { | 
| 439 *result = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x) | 417 *result = IsLess<T, U>::Test(x, y) ? static_cast<result_type>(x) | 
| 440 : static_cast<result_type>(y); | 418 : static_cast<result_type>(y); | 
| 441 return true; | 419 return true; | 
| 442 } | 420 } | 
| 443 }; | 421 }; | 
| 444 | 422 | 
| 445 // This is just boilerplate that wraps the standard floating point arithmetic. | 423 // This is just boilerplate that wraps the standard floating point arithmetic. | 
| 446 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. | 424 // A macro isn't the nicest solution, but it beats rewriting these repeatedly. | 
| 447 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ | 425 #define BASE_FLOAT_ARITHMETIC_OPS(NAME, OP) \ | 
| 448 template <typename T, typename U> \ | 426 template <typename T, typename U> \ | 
| 449 struct Checked##NAME##Op< \ | 427 struct Checked##NAME##Op< \ | 
| 450 T, U, typename std::enable_if<std::is_floating_point<T>::value || \ | 428 T, U, \ | 
| 451 std::is_floating_point<U>::value>::type> { \ | 429 typename std::enable_if<std::is_floating_point<T>::value || \ | 
| 452 using result_type = typename MaxExponentPromotion<T, U>::type; \ | 430 std::is_floating_point<U>::value>::type> { \ | 
| 453 template <typename V> \ | 431 using result_type = typename MaxExponentPromotion<T, U>::type; \ | 
| 454 static bool Do(T x, U y, V* result) { \ | 432 template <typename V> \ | 
| 455 using Promotion = typename MaxExponentPromotion<T, U>::type; \ | 433 static bool Do(T x, U y, V* result) { \ | 
| 456 Promotion presult = x OP y; \ | 434 using Promotion = typename MaxExponentPromotion<T, U>::type; \ | 
| 457 *result = static_cast<V>(presult); \ | 435 Promotion presult = x OP y; \ | 
| 458 return IsValueInRangeForNumericType<V>(presult); \ | 436 *result = static_cast<V>(presult); \ | 
| 459 } \ | 437 return IsValueInRangeForNumericType<V>(presult); \ | 
| 438 } \ | |
| 460 }; | 439 }; | 
| 461 | 440 | 
| 462 BASE_FLOAT_ARITHMETIC_OPS(Add, +) | 441 BASE_FLOAT_ARITHMETIC_OPS(Add, +) | 
| 463 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) | 442 BASE_FLOAT_ARITHMETIC_OPS(Sub, -) | 
| 464 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) | 443 BASE_FLOAT_ARITHMETIC_OPS(Mul, *) | 
| 465 BASE_FLOAT_ARITHMETIC_OPS(Div, /) | 444 BASE_FLOAT_ARITHMETIC_OPS(Div, /) | 
| 466 | 445 | 
| 467 #undef BASE_FLOAT_ARITHMETIC_OPS | 446 #undef BASE_FLOAT_ARITHMETIC_OPS | 
| 468 | 447 | 
| 469 // Wrap the unary operations to allow SFINAE when instantiating integrals versus | |
| 470 // floating points. These don't perform any overflow checking. Rather, they | |
| 471 // exhibit well-defined overflow semantics and rely on the caller to detect | |
| 472 // if an overflow occured. | |
| 473 | |
| 474 template <typename T, | |
| 475 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> | |
| 476 constexpr T NegateWrapper(T value) { | |
| 477 using UnsignedT = typename std::make_unsigned<T>::type; | |
| 478 // This will compile to a NEG on Intel, and is normal negation on ARM. | |
| 479 return static_cast<T>(UnsignedT(0) - static_cast<UnsignedT>(value)); | |
| 480 } | |
| 481 | |
| 482 template < | |
| 483 typename T, | |
| 484 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> | |
| 485 constexpr T NegateWrapper(T value) { | |
| 486 return -value; | |
| 487 } | |
| 488 | |
| 489 template <typename T, | |
| 490 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> | |
| 491 constexpr typename std::make_unsigned<T>::type InvertWrapper(T value) { | |
| 492 return ~value; | |
| 493 } | |
| 494 | |
| 495 template <typename T, | |
| 496 typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> | |
| 497 constexpr T AbsWrapper(T value) { | |
| 498 return static_cast<T>(SafeUnsignedAbs(value)); | |
| 499 } | |
| 500 | |
| 501 template < | |
| 502 typename T, | |
| 503 typename std::enable_if<std::is_floating_point<T>::value>::type* = nullptr> | |
| 504 constexpr T AbsWrapper(T value) { | |
| 505 return value < 0 ? -value : value; | |
| 506 } | |
| 507 | |
| 508 // Floats carry around their validity state with them, but integers do not. So, | 448 // Floats carry around their validity state with them, but integers do not. So, | 
| 509 // we wrap the underlying value in a specialization in order to hide that detail | 449 // we wrap the underlying value in a specialization in order to hide that detail | 
| 510 // and expose an interface via accessors. | 450 // and expose an interface via accessors. | 
| 511 enum NumericRepresentation { | 451 enum NumericRepresentation { | 
| 512 NUMERIC_INTEGER, | 452 NUMERIC_INTEGER, | 
| 513 NUMERIC_FLOATING, | 453 NUMERIC_FLOATING, | 
| 514 NUMERIC_UNKNOWN | 454 NUMERIC_UNKNOWN | 
| 515 }; | 455 }; | 
| 516 | 456 | 
| 517 template <typename NumericType> | 457 template <typename NumericType> | 
| 518 struct GetNumericRepresentation { | 458 struct GetNumericRepresentation { | 
| 519 static const NumericRepresentation value = | 459 static const NumericRepresentation value = | 
| 520 std::is_integral<NumericType>::value | 460 std::is_integral<NumericType>::value | 
| 521 ? NUMERIC_INTEGER | 461 ? NUMERIC_INTEGER | 
| 522 : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING | 462 : (std::is_floating_point<NumericType>::value ? NUMERIC_FLOATING | 
| 523 : NUMERIC_UNKNOWN); | 463 : NUMERIC_UNKNOWN); | 
| 524 }; | 464 }; | 
| 525 | 465 | 
| 526 template <typename T, NumericRepresentation type = | 466 template <typename T, | 
| 527 GetNumericRepresentation<T>::value> | 467 NumericRepresentation type = GetNumericRepresentation<T>::value> | 
| 528 class CheckedNumericState {}; | 468 class CheckedNumericState {}; | 
| 529 | 469 | 
| 530 // Integrals require quite a bit of additional housekeeping to manage state. | 470 // Integrals require quite a bit of additional housekeeping to manage state. | 
| 531 template <typename T> | 471 template <typename T> | 
| 532 class CheckedNumericState<T, NUMERIC_INTEGER> { | 472 class CheckedNumericState<T, NUMERIC_INTEGER> { | 
| 533 private: | 473 private: | 
| 534 // is_valid_ precedes value_ because member intializers in the constructors | 474 // is_valid_ precedes value_ because member intializers in the constructors | 
| 535 // are evaluated in field order, and is_valid_ must be read when initializing | 475 // are evaluated in field order, and is_valid_ must be read when initializing | 
| 536 // value_. | 476 // value_. | 
| 537 bool is_valid_; | 477 bool is_valid_; | 
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 618 | 558 | 
| 619 constexpr bool is_valid() const { | 559 constexpr bool is_valid() const { | 
| 620 // Written this way because std::isfinite is not reliably constexpr. | 560 // Written this way because std::isfinite is not reliably constexpr. | 
| 621 // TODO(jschuh): Fix this if the libraries ever get fixed. | 561 // TODO(jschuh): Fix this if the libraries ever get fixed. | 
| 622 return value_ <= std::numeric_limits<T>::max() && | 562 return value_ <= std::numeric_limits<T>::max() && | 
| 623 value_ >= std::numeric_limits<T>::lowest(); | 563 value_ >= std::numeric_limits<T>::lowest(); | 
| 624 } | 564 } | 
| 625 constexpr T value() const { return value_; } | 565 constexpr T value() const { return value_; } | 
| 626 }; | 566 }; | 
| 627 | 567 | 
| 628 template <template <typename, typename, typename> class M, | |
| 629 typename L, | |
| 630 typename R> | |
| 631 struct MathWrapper { | |
| 632 using math = M<typename UnderlyingType<L>::type, | |
| 633 typename UnderlyingType<R>::type, | |
| 634 void>; | |
| 635 using type = typename math::result_type; | |
| 636 }; | |
| 637 | |
| 638 } // namespace internal | 568 } // namespace internal | 
| 639 } // namespace base | 569 } // namespace base | 
| 640 | 570 | 
| 641 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 571 #endif // BASE_NUMERICS_CHECKED_MATH_IMPL_H_ | 
| OLD | NEW |