Chromium Code Reviews| 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_CONVERSIONS_H_ | 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 
| 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 
| 7 | 7 | 
| 8 #include <stddef.h> | 8 #include <stddef.h> | 
| 9 | 9 | 
| 10 #include <limits> | 10 #include <limits> | 
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 48 // the supplied arithmetic or StrictNumeric type. | 48 // the supplied arithmetic or StrictNumeric type. | 
| 49 | 49 | 
| 50 // Convenience function that returns true if the supplied value is in range | 50 // Convenience function that returns true if the supplied value is in range | 
| 51 // for the destination type. | 51 // for the destination type. | 
| 52 template <typename Dst, typename Src> | 52 template <typename Dst, typename Src> | 
| 53 constexpr bool IsValueInRangeForNumericType(Src value) { | 53 constexpr bool IsValueInRangeForNumericType(Src value) { | 
| 54 return internal::DstRangeRelationToSrcRange<Dst>(value) == | 54 return internal::DstRangeRelationToSrcRange<Dst>(value) == | 
| 55 internal::RANGE_VALID; | 55 internal::RANGE_VALID; | 
| 56 } | 56 } | 
| 57 | 57 | 
| 58 // Convenience function for determining if a numeric value is negative without | |
| 59 // throwing compiler warnings on: unsigned(value) < 0. | |
| 60 template <typename T, | |
| 61 typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> | |
| 62 constexpr bool IsValueNegative(T value) { | |
| 63 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); | |
| 64 return value < 0; | |
| 65 } | |
| 66 | |
| 67 template <typename T, | |
| 68 typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> | |
| 69 constexpr bool IsValueNegative(T) { | |
| 70 static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); | |
| 71 return false; | |
| 72 } | |
| 73 | |
| 74 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. | 58 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. | 
| 75 struct CheckOnFailure { | 59 struct CheckOnFailure { | 
| 76 template <typename T> | 60 template <typename T> | 
| 77 static T HandleFailure() { | 61 static T HandleFailure() { | 
| 78 #if defined(__GNUC__) || defined(__clang__) | 62 #if defined(__GNUC__) || defined(__clang__) | 
| 79 __builtin_trap(); | 63 __builtin_trap(); | 
| 80 #else | 64 #else | 
| 81 ((void)(*(volatile char*)0 = 0)); | 65 ((void)(*(volatile char*)0 = 0)); | 
| 82 #endif | 66 #endif | 
| 83 return T(); | 67 return T(); | 
| 84 } | 68 } | 
| 85 }; | 69 }; | 
| 86 | 70 | 
| 87 // checked_cast<> is analogous to static_cast<> for numeric types, | 71 // checked_cast<> is analogous to static_cast<> for numeric types, | 
| 88 // except that it CHECKs that the specified numeric conversion will not | 72 // except that it CHECKs that the specified numeric conversion will not | 
| 89 // overflow or underflow. NaN source will always trigger a CHECK. | 73 // overflow or underflow. NaN source will always trigger a CHECK. | 
| 90 template <typename Dst, | 74 template <typename Dst, | 
| 91 class CheckHandler = CheckOnFailure, | 75 class CheckHandler = CheckOnFailure, | 
| 92 typename Src> | 76 typename Src> | 
| 93 constexpr Dst checked_cast(Src value) { | 77 constexpr Dst checked_cast(Src value) { | 
| 94 // This throws a compile-time error on evaluating the constexpr if it can be | 78 // This throws a compile-time error on evaluating the constexpr if it can be | 
| 95 // determined at compile-time as failing, otherwise it will CHECK at runtime. | 79 // determined at compile-time as failing, otherwise it will CHECK at runtime. | 
| 96 using SrcType = typename internal::UnderlyingType<Src>::type; | 80 using SrcType = typename internal::UnderlyingType<Src>::type; | 
| 97 return IsValueInRangeForNumericType<Dst, SrcType>(value) | 81 return IsValueInRangeForNumericType<Dst, SrcType>(value) | 
| 98 ? static_cast<Dst>(static_cast<SrcType>(value)) | 82 ? static_cast<Dst>(static_cast<SrcType>(value)) | 
| 99 : CheckHandler::template HandleFailure<Dst>(); | 83 : CheckHandler::template HandleFailure<Dst>(); | 
| 100 } | 84 } | 
| 101 | 85 | 
| 102 // HandleNaN will return 0 in this case. | 86 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. | 
| 103 struct SaturatedCastNaNBehaviorReturnZero { | 87 template <typename T> | 
| 104 template <typename T> | 88 struct SaturatedCastDefaultHandler { | 
| 105 static constexpr T HandleFailure() { | 89 static constexpr T HandleNaN() { | 
| 106 return T(); | 90 return std::numeric_limits<T>::has_quiet_NaN | 
| 91 ? std::numeric_limits<T>::quiet_NaN() | |
| 92 : T(); | |
| 93 } | |
| 94 static constexpr T max() { return std::numeric_limits<T>::max(); } | |
| 95 static constexpr T HandleOverflow() { | |
| 96 return std::numeric_limits<T>::has_infinity | |
| 97 ? std::numeric_limits<T>::infinity() | |
| 98 : std::numeric_limits<T>::max(); | |
| 99 } | |
| 100 static constexpr T lowest() { return std::numeric_limits<T>::lowest(); } | |
| 101 static constexpr T HandleUnderflow() { | |
| 102 return std::numeric_limits<T>::has_infinity | |
| 103 ? std::numeric_limits<T>::infinity() * -1 | |
| 104 : std::numeric_limits<T>::lowest(); | |
| 107 } | 105 } | 
| 108 }; | 106 }; | 
| 109 | 107 | 
| 110 namespace internal { | 108 namespace internal { | 
| 111 // These wrappers are used for C++11 constexpr support by avoiding both the | 109 // saturated_cast<> is analogous to static_cast<> for numeric types, except | 
| 
 
eae
2016/12/15 23:07:46
This is awesome! I'd love to replace the clampTo<T
 
jschuh
2016/12/16 00:31:00
Thanks, sgtm.
 
 | |
| 112 // declaration of local variables and invalid evaluation resulting from the | 110 // that the specified numeric conversion will saturate by default rather than | 
| 113 // lack of "constexpr if" support in the saturated_cast template function. | 111 // overflow or underflow, and NaN assignment to an integral will return 0. | 
| 114 // TODO(jschuh): Convert to single function with a switch once we support C++14. | 112 // All boundary condition behaviors can be overriden with a custom handler. | 
| 115 template < | |
| 116 typename Dst, | |
| 117 class NaNHandler, | |
| 118 typename Src, | |
| 119 typename std::enable_if<std::is_integral<Dst>::value>::type* = nullptr> | |
| 120 constexpr Dst saturated_cast_impl(const Src value, | |
| 121 const RangeConstraint constraint) { | |
| 122 return constraint == RANGE_VALID | |
| 123 ? static_cast<Dst>(value) | |
| 124 : (constraint == RANGE_UNDERFLOW | |
| 125 ? std::numeric_limits<Dst>::lowest() | |
| 126 : (constraint == RANGE_OVERFLOW | |
| 127 ? std::numeric_limits<Dst>::max() | |
| 128 : NaNHandler::template HandleFailure<Dst>())); | |
| 129 } | |
| 130 | |
| 131 template <typename Dst, | 113 template <typename Dst, | 
| 132 class NaNHandler, | 114 template <typename> | 
| 133 typename Src, | 115 class SaturationHandler = SaturatedCastDefaultHandler, | 
| 134 typename std::enable_if<std::is_floating_point<Dst>::value>::type* = | |
| 135 nullptr> | |
| 136 constexpr Dst saturated_cast_impl(const Src value, | |
| 137 const RangeConstraint constraint) { | |
| 138 return constraint == RANGE_VALID | |
| 139 ? static_cast<Dst>(value) | |
| 140 : (constraint == RANGE_UNDERFLOW | |
| 141 ? -std::numeric_limits<Dst>::infinity() | |
| 142 : (constraint == RANGE_OVERFLOW | |
| 143 ? std::numeric_limits<Dst>::infinity() | |
| 144 : std::numeric_limits<Dst>::quiet_NaN())); | |
| 145 } | |
| 146 | |
| 147 // saturated_cast<> is analogous to static_cast<> for numeric types, except | |
| 148 // that the specified numeric conversion will saturate rather than overflow or | |
| 149 // underflow. NaN assignment to an integral will defer the behavior to a | |
| 150 // specified class. By default, it will return 0. | |
| 151 template <typename Dst, | |
| 152 class NaNHandler = SaturatedCastNaNBehaviorReturnZero, | |
| 153 typename Src> | 116 typename Src> | 
| 154 constexpr Dst saturated_cast(Src value) { | 117 constexpr Dst saturated_cast(Src value) { | 
| 118 static_assert( | |
| 119 SaturationHandler<Dst>::lowest() < SaturationHandler<Dst>::max(), ""); | |
| 155 using SrcType = typename UnderlyingType<Src>::type; | 120 using SrcType = typename UnderlyingType<Src>::type; | 
| 156 return internal::saturated_cast_impl<Dst, NaNHandler>( | 121 return IsGreaterOrEqual<SrcType, Dst>::Test( | 
| 
 
eae
2016/12/15 23:07:46
Am I right in assuming that all these branches com
 
jschuh
2016/12/16 00:31:00
Yes. All of the additional craziness is to cover a
 
 | |
| 157 value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value)); | 122 value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest()) | 
| 123 ? (IsLessOrEqual<SrcType, Dst>::Test( | |
| 124 value, | |
| 125 NarrowingRange<Dst, SrcType, SaturationHandler>::max()) | |
| 126 ? static_cast<Dst>(value) | |
| 127 : SaturationHandler<Dst>::HandleOverflow()) | |
| 128 : (std::is_integral<SrcType>::value || | |
| 129 std::is_floating_point<Dst>::value || | |
| 130 IsLessOrEqual<SrcType, Dst>::Test( | |
| 131 value, NarrowingRange<Dst, SrcType, | |
| 132 SaturationHandler>::max()) | |
| 133 ? SaturationHandler<Dst>::HandleUnderflow() | |
| 134 : SaturationHandler<Dst>::HandleNaN()); | |
| 158 } | 135 } | 
| 159 | 136 | 
| 160 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 137 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 
| 161 // it will cause a compile failure if the destination type is not large enough | 138 // it will cause a compile failure if the destination type is not large enough | 
| 162 // to contain any value in the source type. It performs no runtime checking. | 139 // to contain any value in the source type. It performs no runtime checking. | 
| 163 template <typename Dst, typename Src> | 140 template <typename Dst, typename Src> | 
| 164 constexpr Dst strict_cast(Src value) { | 141 constexpr Dst strict_cast(Src value) { | 
| 165 using SrcType = typename UnderlyingType<Src>::type; | 142 using SrcType = typename UnderlyingType<Src>::type; | 
| 166 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); | 143 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); | 
| 167 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 144 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 278 STRICT_COMPARISON_OP(IsNotEqual, !=); | 255 STRICT_COMPARISON_OP(IsNotEqual, !=); | 
| 279 | 256 | 
| 280 #undef STRICT_COMPARISON_OP | 257 #undef STRICT_COMPARISON_OP | 
| 281 }; | 258 }; | 
| 282 | 259 | 
| 283 using internal::strict_cast; | 260 using internal::strict_cast; | 
| 284 using internal::saturated_cast; | 261 using internal::saturated_cast; | 
| 285 using internal::SafeUnsignedAbs; | 262 using internal::SafeUnsignedAbs; | 
| 286 using internal::StrictNumeric; | 263 using internal::StrictNumeric; | 
| 287 using internal::MakeStrictNum; | 264 using internal::MakeStrictNum; | 
| 265 using internal::IsValueNegative; | |
| 288 | 266 | 
| 289 // Explicitly make a shorter size_t alias for convenience. | 267 // Explicitly make a shorter size_t alias for convenience. | 
| 290 using SizeT = StrictNumeric<size_t>; | 268 using SizeT = StrictNumeric<size_t>; | 
| 291 | 269 | 
| 292 } // namespace base | 270 } // namespace base | 
| 293 | 271 | 
| 294 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 272 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 
| OLD | NEW |