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 |
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(), ""); |
| 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. |
155 using SrcType = typename UnderlyingType<Src>::type; | 125 using SrcType = typename UnderlyingType<Src>::type; |
156 return internal::saturated_cast_impl<Dst, NaNHandler>( | 126 return IsGreaterOrEqual<SrcType, Dst>::Test( |
157 value, internal::DstRangeRelationToSrcRange<Dst, SrcType>(value)); | 127 value, NarrowingRange<Dst, SrcType, SaturationHandler>::lowest()) |
| 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()); |
158 } | 142 } |
159 | 143 |
160 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 144 // 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 | 145 // 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. | 146 // to contain any value in the source type. It performs no runtime checking. |
163 template <typename Dst, typename Src> | 147 template <typename Dst, typename Src> |
164 constexpr Dst strict_cast(Src value) { | 148 constexpr Dst strict_cast(Src value) { |
165 using SrcType = typename UnderlyingType<Src>::type; | 149 using SrcType = typename UnderlyingType<Src>::type; |
166 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); | 150 static_assert(UnderlyingType<Src>::is_numeric, "Argument must be numeric."); |
167 static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); | 151 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, !=); | 262 STRICT_COMPARISON_OP(IsNotEqual, !=); |
279 | 263 |
280 #undef STRICT_COMPARISON_OP | 264 #undef STRICT_COMPARISON_OP |
281 }; | 265 }; |
282 | 266 |
283 using internal::strict_cast; | 267 using internal::strict_cast; |
284 using internal::saturated_cast; | 268 using internal::saturated_cast; |
285 using internal::SafeUnsignedAbs; | 269 using internal::SafeUnsignedAbs; |
286 using internal::StrictNumeric; | 270 using internal::StrictNumeric; |
287 using internal::MakeStrictNum; | 271 using internal::MakeStrictNum; |
| 272 using internal::IsValueNegative; |
288 | 273 |
289 // Explicitly make a shorter size_t alias for convenience. | 274 // Explicitly make a shorter size_t alias for convenience. |
290 using SizeT = StrictNumeric<size_t>; | 275 using SizeT = StrictNumeric<size_t>; |
291 | 276 |
292 } // namespace base | 277 } // namespace base |
293 | 278 |
294 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 279 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
OLD | NEW |