| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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_SAFE_MATH_IMPL_H_ |
| 6 #define BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 6 #define BASE_NUMERICS_SAFE_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 | 18 |
| 19 namespace base { | 19 namespace base { |
| 20 namespace internal { | 20 namespace internal { |
| 21 | 21 |
| 22 // Everything from here up to the floating point operations is portable C++, | 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 | 23 // but it may not be fast. This code could be split based on |
| 24 // platform/architecture and replaced with potentially faster implementations. | 24 // platform/architecture and replaced with potentially faster implementations. |
| 25 | 25 |
| 26 // Integer promotion templates used by the portable checked integer arithmetic. | |
| 27 template <size_t Size, bool IsSigned> | |
| 28 struct IntegerForSizeAndSign; | |
| 29 template <> | |
| 30 struct IntegerForSizeAndSign<1, true> { | |
| 31 typedef int8_t type; | |
| 32 }; | |
| 33 template <> | |
| 34 struct IntegerForSizeAndSign<1, false> { | |
| 35 typedef uint8_t type; | |
| 36 }; | |
| 37 template <> | |
| 38 struct IntegerForSizeAndSign<2, true> { | |
| 39 typedef int16_t type; | |
| 40 }; | |
| 41 template <> | |
| 42 struct IntegerForSizeAndSign<2, false> { | |
| 43 typedef uint16_t type; | |
| 44 }; | |
| 45 template <> | |
| 46 struct IntegerForSizeAndSign<4, true> { | |
| 47 typedef int32_t type; | |
| 48 }; | |
| 49 template <> | |
| 50 struct IntegerForSizeAndSign<4, false> { | |
| 51 typedef uint32_t type; | |
| 52 }; | |
| 53 template <> | |
| 54 struct IntegerForSizeAndSign<8, true> { | |
| 55 typedef int64_t type; | |
| 56 }; | |
| 57 template <> | |
| 58 struct IntegerForSizeAndSign<8, false> { | |
| 59 typedef uint64_t type; | |
| 60 static_assert(sizeof(uintmax_t) == 8, | |
| 61 "Max integer size not supported for this toolchain."); | |
| 62 }; | |
| 63 | |
| 64 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to | |
| 65 // support 128-bit math, then the ArithmeticPromotion template below will need | |
| 66 // to be updated (or more likely replaced with a decltype expression). | |
| 67 | |
| 68 template <typename Integer> | |
| 69 struct UnsignedIntegerForSize { | |
| 70 typedef typename std::enable_if< | |
| 71 std::numeric_limits<Integer>::is_integer, | |
| 72 typename IntegerForSizeAndSign<sizeof(Integer), false>::type>::type type; | |
| 73 }; | |
| 74 | |
| 75 template <typename Integer> | |
| 76 struct SignedIntegerForSize { | |
| 77 typedef typename std::enable_if< | |
| 78 std::numeric_limits<Integer>::is_integer, | |
| 79 typename IntegerForSizeAndSign<sizeof(Integer), true>::type>::type type; | |
| 80 }; | |
| 81 | |
| 82 template <typename Integer> | |
| 83 struct TwiceWiderInteger { | |
| 84 typedef typename std::enable_if< | |
| 85 std::numeric_limits<Integer>::is_integer, | |
| 86 typename IntegerForSizeAndSign< | |
| 87 sizeof(Integer) * 2, | |
| 88 std::numeric_limits<Integer>::is_signed>::type>::type type; | |
| 89 }; | |
| 90 | |
| 91 template <typename Integer> | |
| 92 struct PositionOfSignBit { | |
| 93 static const typename std::enable_if<std::numeric_limits<Integer>::is_integer, | |
| 94 size_t>::type value = | |
| 95 CHAR_BIT * sizeof(Integer) - 1; | |
| 96 }; | |
| 97 | |
| 98 // This is used for UnsignedAbs, where we need to support floating-point | 26 // This is used for UnsignedAbs, where we need to support floating-point |
| 99 // template instantiations even though we don't actually support the operations. | 27 // template instantiations even though we don't actually support the operations. |
| 100 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, | 28 // However, there is no corresponding implementation of e.g. SafeUnsignedAbs, |
| 101 // so the float versions will not compile. | 29 // so the float versions will not compile. |
| 102 template <typename Numeric, | 30 template <typename Numeric, |
| 103 bool IsInteger = std::numeric_limits<Numeric>::is_integer, | 31 bool IsInteger = std::numeric_limits<Numeric>::is_integer, |
| 104 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> | 32 bool IsFloat = std::numeric_limits<Numeric>::is_iec559> |
| 105 struct UnsignedOrFloatForSize; | 33 struct UnsignedOrFloatForSize; |
| 106 | 34 |
| 107 template <typename Numeric> | 35 template <typename Numeric> |
| (...skipping 14 matching lines...) Expand all Loading... |
| 122 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> | 50 return !!(static_cast<typename UnsignedIntegerForSize<T>::type>(x) >> |
| 123 PositionOfSignBit<T>::value); | 51 PositionOfSignBit<T>::value); |
| 124 } | 52 } |
| 125 | 53 |
| 126 // This wrapper undoes the standard integer promotions. | 54 // This wrapper undoes the standard integer promotions. |
| 127 template <typename T> | 55 template <typename T> |
| 128 constexpr T BinaryComplement(T x) { | 56 constexpr T BinaryComplement(T x) { |
| 129 return static_cast<T>(~x); | 57 return static_cast<T>(~x); |
| 130 } | 58 } |
| 131 | 59 |
| 132 enum ArithmeticPromotionCategory { | |
| 133 LEFT_PROMOTION, // Use the type of the left-hand argument. | |
| 134 RIGHT_PROMOTION, // Use the type of the right-hand argument. | |
| 135 MAX_EXPONENT_PROMOTION, // Use the type supporting the largest exponent. | |
| 136 BIG_ENOUGH_PROMOTION // Attempt to find a big enough type. | |
| 137 }; | |
| 138 | |
| 139 template <ArithmeticPromotionCategory Promotion, | |
| 140 typename Lhs, | |
| 141 typename Rhs = Lhs> | |
| 142 struct ArithmeticPromotion; | |
| 143 | |
| 144 template <typename Lhs, | |
| 145 typename Rhs, | |
| 146 ArithmeticPromotionCategory Promotion = | |
| 147 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) | |
| 148 ? LEFT_PROMOTION | |
| 149 : RIGHT_PROMOTION> | |
| 150 struct MaxExponentPromotion; | |
| 151 | |
| 152 template <typename Lhs, typename Rhs> | |
| 153 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { | |
| 154 using type = Lhs; | |
| 155 }; | |
| 156 | |
| 157 template <typename Lhs, typename Rhs> | |
| 158 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { | |
| 159 using type = Rhs; | |
| 160 }; | |
| 161 | |
| 162 template <typename Lhs, | |
| 163 typename Rhs = Lhs, | |
| 164 bool is_intmax_type = | |
| 165 std::is_integral< | |
| 166 typename MaxExponentPromotion<Lhs, Rhs>::type>::value && | |
| 167 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) == | |
| 168 sizeof(intmax_t), | |
| 169 bool is_max_exponent = | |
| 170 StaticDstRangeRelationToSrcRange< | |
| 171 typename MaxExponentPromotion<Lhs, Rhs>::type, | |
| 172 Lhs>::value == | |
| 173 NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< | |
| 174 typename MaxExponentPromotion<Lhs, Rhs>::type, | |
| 175 Rhs>::value == NUMERIC_RANGE_CONTAINED> | |
| 176 struct BigEnoughPromotion; | |
| 177 | |
| 178 // The side with the max exponent is big enough. | |
| 179 template <typename Lhs, typename Rhs, bool is_intmax_type> | |
| 180 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { | |
| 181 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | |
| 182 static const bool is_contained = true; | |
| 183 }; | |
| 184 | |
| 185 // We can use a twice wider type to fit. | |
| 186 template <typename Lhs, typename Rhs> | |
| 187 struct BigEnoughPromotion<Lhs, Rhs, false, false> { | |
| 188 using type = typename IntegerForSizeAndSign< | |
| 189 sizeof(typename MaxExponentPromotion<Lhs, Rhs>::type) * 2, | |
| 190 std::is_signed<Lhs>::value || std::is_signed<Rhs>::value>::type; | |
| 191 static const bool is_contained = true; | |
| 192 }; | |
| 193 | |
| 194 // No type is large enough. | |
| 195 template <typename Lhs, typename Rhs> | |
| 196 struct BigEnoughPromotion<Lhs, Rhs, true, false> { | |
| 197 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | |
| 198 static const bool is_contained = false; | |
| 199 }; | |
| 200 | |
| 201 // These are the supported promotion types. | |
| 202 | |
| 203 // Use the type supporting the largest exponent. | |
| 204 template <typename Lhs, typename Rhs> | |
| 205 struct ArithmeticPromotion<MAX_EXPONENT_PROMOTION, Lhs, Rhs> { | |
| 206 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; | |
| 207 static const bool is_contained = true; | |
| 208 }; | |
| 209 | |
| 210 // Attempt to find a big enough type. | |
| 211 template <typename Lhs, typename Rhs> | |
| 212 struct ArithmeticPromotion<BIG_ENOUGH_PROMOTION, Lhs, Rhs> { | |
| 213 using type = typename BigEnoughPromotion<Lhs, Rhs>::type; | |
| 214 static const bool is_contained = BigEnoughPromotion<Lhs, Rhs>::is_contained; | |
| 215 }; | |
| 216 | |
| 217 // We can statically check if operations on the provided types can wrap, so we | |
| 218 // can skip the checked operations if they're not needed. So, for an integer we | |
| 219 // care if the destination type preserves the sign and is twice the width of | |
| 220 // the source. | |
| 221 template <typename T, typename Lhs, typename Rhs> | |
| 222 struct IsIntegerArithmeticSafe { | |
| 223 static const bool value = !std::numeric_limits<T>::is_iec559 && | |
| 224 StaticDstRangeRelationToSrcRange<T, Lhs>::value == | |
| 225 NUMERIC_RANGE_CONTAINED && | |
| 226 sizeof(T) >= (2 * sizeof(Lhs)) && | |
| 227 StaticDstRangeRelationToSrcRange<T, Rhs>::value != | |
| 228 NUMERIC_RANGE_CONTAINED && | |
| 229 sizeof(T) >= (2 * sizeof(Rhs)); | |
| 230 }; | |
| 231 | |
| 232 // Probe for builtin math overflow support on Clang and version check on GCC. | 60 // Probe for builtin math overflow support on Clang and version check on GCC. |
| 233 #if defined(__has_builtin) | 61 #if defined(__has_builtin) |
| 234 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) | 62 #define USE_OVERFLOW_BUILTINS (__has_builtin(__builtin_add_overflow)) |
| 235 #elif defined(__GNUC__) | 63 #elif defined(__GNUC__) |
| 236 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) | 64 #define USE_OVERFLOW_BUILTINS (__GNUC__ >= 5) |
| 237 #else | 65 #else |
| 238 #define USE_OVERFLOW_BUILTINS (0) | 66 #define USE_OVERFLOW_BUILTINS (0) |
| 239 #endif | 67 #endif |
| 240 | 68 |
| 241 // Here are the actual portable checked integer math implementations. | 69 // Here are the actual portable checked integer math implementations. |
| (...skipping 623 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 865 | 693 |
| 866 // Copy constructor. | 694 // Copy constructor. |
| 867 template <typename Src> | 695 template <typename Src> |
| 868 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) | 696 constexpr CheckedNumericState(const CheckedNumericState<Src>& rhs) |
| 869 : value_(static_cast<T>(rhs.value())) {} | 697 : value_(static_cast<T>(rhs.value())) {} |
| 870 | 698 |
| 871 constexpr bool is_valid() const { return std::isfinite(value_); } | 699 constexpr bool is_valid() const { return std::isfinite(value_); } |
| 872 constexpr T value() const { return value_; } | 700 constexpr T value() const { return value_; } |
| 873 }; | 701 }; |
| 874 | 702 |
| 875 // The following are helper templates used in the CheckedNumeric class. | |
| 876 template <typename T> | |
| 877 class CheckedNumeric; | |
| 878 | |
| 879 // Used to treat CheckedNumeric and arithmetic underlying types the same. | |
| 880 template <typename T> | |
| 881 struct UnderlyingType { | |
| 882 using type = T; | |
| 883 static const bool is_numeric = std::is_arithmetic<T>::value; | |
| 884 static const bool is_checked = false; | |
| 885 }; | |
| 886 | |
| 887 template <typename T> | |
| 888 struct UnderlyingType<CheckedNumeric<T>> { | |
| 889 using type = T; | |
| 890 static const bool is_numeric = true; | |
| 891 static const bool is_checked = true; | |
| 892 }; | |
| 893 | |
| 894 template <typename L, typename R> | |
| 895 struct IsCheckedOp { | |
| 896 static const bool value = | |
| 897 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && | |
| 898 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); | |
| 899 }; | |
| 900 | |
| 901 template <template <typename, typename, typename> class M, | 703 template <template <typename, typename, typename> class M, |
| 902 typename L, | 704 typename L, |
| 903 typename R> | 705 typename R> |
| 904 struct MathWrapper { | 706 struct MathWrapper { |
| 905 using math = M<typename UnderlyingType<L>::type, | 707 using math = M<typename UnderlyingType<L>::type, |
| 906 typename UnderlyingType<R>::type, | 708 typename UnderlyingType<R>::type, |
| 907 void>; | 709 void>; |
| 908 using type = typename math::result_type; | 710 using type = typename math::result_type; |
| 909 }; | 711 }; |
| 910 | 712 |
| 911 } // namespace internal | 713 } // namespace internal |
| 912 } // namespace base | 714 } // namespace base |
| 913 | 715 |
| 914 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ | 716 #endif // BASE_NUMERICS_SAFE_MATH_IMPL_H_ |
| OLD | NEW |