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 |
58 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. | 74 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. |
59 struct CheckOnFailure { | 75 struct CheckOnFailure { |
60 template <typename T> | 76 template <typename T> |
61 static T HandleFailure() { | 77 static T HandleFailure() { |
62 #if defined(__GNUC__) || defined(__clang__) | 78 #if defined(__GNUC__) || defined(__clang__) |
63 __builtin_trap(); | 79 __builtin_trap(); |
64 #else | 80 #else |
65 ((void)(*(volatile char*)0 = 0)); | 81 ((void)(*(volatile char*)0 = 0)); |
66 #endif | 82 #endif |
67 return T(); | 83 return T(); |
68 } | 84 } |
69 }; | 85 }; |
70 | 86 |
71 // checked_cast<> is analogous to static_cast<> for numeric types, | 87 // checked_cast<> is analogous to static_cast<> for numeric types, |
72 // except that it CHECKs that the specified numeric conversion will not | 88 // except that it CHECKs that the specified numeric conversion will not |
73 // overflow or underflow. NaN source will always trigger a CHECK. | 89 // overflow or underflow. NaN source will always trigger a CHECK. |
74 template <typename Dst, | 90 template <typename Dst, |
75 class CheckHandler = CheckOnFailure, | 91 class CheckHandler = CheckOnFailure, |
76 typename Src> | 92 typename Src> |
77 constexpr Dst checked_cast(Src value) { | 93 constexpr Dst checked_cast(Src value) { |
78 // This throws a compile-time error on evaluating the constexpr if it can be | 94 // This throws a compile-time error on evaluating the constexpr if it can be |
79 // determined at compile-time as failing, otherwise it will CHECK at runtime. | 95 // determined at compile-time as failing, otherwise it will CHECK at runtime. |
80 using SrcType = typename internal::UnderlyingType<Src>::type; | 96 using SrcType = typename internal::UnderlyingType<Src>::type; |
81 return IsValueInRangeForNumericType<Dst, SrcType>(value) | 97 return IsValueInRangeForNumericType<Dst, SrcType>(value) |
82 ? static_cast<Dst>(static_cast<SrcType>(value)) | 98 ? static_cast<Dst>(static_cast<SrcType>(value)) |
83 : CheckHandler::template HandleFailure<Dst>(); | 99 : CheckHandler::template HandleFailure<Dst>(); |
84 } | 100 } |
85 | 101 |
86 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. | 102 // HandleNaN will return 0 in this case. |
87 template <typename T> | 103 struct SaturatedCastNaNBehaviorReturnZero { |
88 struct SaturatedCastDefaultHandler { | 104 template <typename T> |
89 static constexpr T HandleNaN() { | 105 static constexpr T HandleFailure() { |
90 return std::numeric_limits<T>::has_quiet_NaN | 106 return T(); |
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(); | |
105 } | 107 } |
106 }; | 108 }; |
107 | 109 |
108 namespace internal { | 110 namespace internal { |
| 111 // These wrappers are used for C++11 constexpr support by avoiding both the |
| 112 // declaration of local variables and invalid evaluation resulting from the |
| 113 // lack of "constexpr if" support in the saturated_cast template function. |
| 114 // TODO(jschuh): Convert to single function with a switch once we support C++14. |
| 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, |
| 132 class NaNHandler, |
| 133 typename Src, |
| 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 |
109 // saturated_cast<> is analogous to static_cast<> for numeric types, except | 147 // saturated_cast<> is analogous to static_cast<> for numeric types, except |
110 // that the specified numeric conversion will saturate by default rather than | 148 // that the specified numeric conversion will saturate rather than overflow or |
111 // overflow or underflow, and NaN assignment to an integral will return 0. | 149 // underflow. NaN assignment to an integral will defer the behavior to a |
112 // All boundary condition behaviors can be overriden with a custom handler. | 150 // specified class. By default, it will return 0. |
113 template <typename Dst, | 151 template <typename Dst, |
114 template <typename> | 152 class NaNHandler = SaturatedCastNaNBehaviorReturnZero, |
115 class SaturationHandler = SaturatedCastDefaultHandler, | |
116 typename Src> | 153 typename Src> |
117 constexpr Dst saturated_cast(Src value) { | 154 constexpr Dst saturated_cast(Src value) { |
118 static_assert( | |
119 SaturationHandler<Dst>::lowest() < SaturationHandler<Dst>::max(), ""); | |
120 // While this looks like a lot of code, it's all constexpr and all but | |
121 // one variable are compile-time constants (enforced by a static_assert). | |
122 // So, it should evaluate to the minimum number of comparisons required | |
123 // for the range check, which is 0-3, depending on the exact source and | |
124 // destination types, and whatever custom range is specified. | |
125 using SrcType = typename UnderlyingType<Src>::type; | 155 using SrcType = typename UnderlyingType<Src>::type; |
126 return IsGreaterOrEqual<SrcType, Dst>::Test( | 156 return internal::saturated_cast_impl<Dst, NaNHandler>( |
127 value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest()) | 157 value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value)); |
128 ? (IsLessOrEqual<SrcType, Dst>::Test( | |
129 value, | |
130 NarrowingRange<Dst, SrcType, SaturationHandler>::max()) | |
131 ? static_cast<Dst>(value) | |
132 : SaturationHandler<Dst>::HandleOverflow()) | |
133 // This last branch is a little confusing. It's specifically to | |
134 // catch NaN when converting from float to integral. | |
135 : (std::is_integral<SrcType>::value || | |
136 std::is_floating_point<Dst>::value || | |
137 IsLessOrEqual<SrcType, Dst>::Test( | |
138 value, NarrowingRange<Dst, SrcType, | |
139 SaturationHandler>::max()) | |
140 ? SaturationHandler<Dst>::HandleUnderflow() | |
141 : SaturationHandler<Dst>::HandleNaN()); | |
142 } | 158 } |
143 | 159 |
144 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 160 // strict_cast<> is analogous to static_cast<> for numeric types, except that |
145 // it will cause a compile failure if the destination type is not large enough | 161 // it will cause a compile failure if the destination type is not large enough |
146 // to contain any value in the source type. It performs no runtime checking. | 162 // to contain any value in the source type. It performs no runtime checking. |
147 template <typename Dst, typename Src> | 163 template <typename Dst, typename Src> |
148 constexpr Dst strict_cast(Src value) { | 164 constexpr Dst strict_cast(Src value) { |
149 using SrcType = typename UnderlyingType<Src>::type; | 165 using SrcType = typename UnderlyingType<Src>::type; |
150 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); | 166 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); |
151 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 167 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
262 STRICT_COMPARISON_OP(IsNotEqual, !=); | 278 STRICT_COMPARISON_OP(IsNotEqual, !=); |
263 | 279 |
264 #undef STRICT_COMPARISON_OP | 280 #undef STRICT_COMPARISON_OP |
265 }; | 281 }; |
266 | 282 |
267 using internal::strict_cast; | 283 using internal::strict_cast; |
268 using internal::saturated_cast; | 284 using internal::saturated_cast; |
269 using internal::SafeUnsignedAbs; | 285 using internal::SafeUnsignedAbs; |
270 using internal::StrictNumeric; | 286 using internal::StrictNumeric; |
271 using internal::MakeStrictNum; | 287 using internal::MakeStrictNum; |
272 using internal::IsValueNegative; | |
273 | 288 |
274 // Explicitly make a shorter size_t alias for convenience. | 289 // Explicitly make a shorter size_t alias for convenience. |
275 using SizeT = StrictNumeric<size_t>; | 290 using SizeT = StrictNumeric<size_t>; |
276 | 291 |
277 } // namespace base | 292 } // namespace base |
278 | 293 |
279 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 294 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
OLD | NEW |