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 <cassert> | 10 #include <cassert> |
11 #include <limits> | 11 #include <limits> |
| 12 #include <ostream> |
12 #include <type_traits> | 13 #include <type_traits> |
13 | 14 |
14 #include "base/numerics/safe_conversions_impl.h" | 15 #include "base/numerics/safe_conversions_impl.h" |
15 | 16 |
16 namespace base { | 17 namespace base { |
17 | 18 |
| 19 // The following are helper constexpr template functions and classes for safely |
| 20 // performing a range of conversions, assignments, and tests: |
| 21 // |
| 22 // checked_cast<> - Analogous to static_cast<> for numeric types, except |
| 23 // that it CHECKs that the specified numeric conversion will not overflow |
| 24 // or underflow. NaN source will always trigger a CHECK. |
| 25 // The default CHECK triggers a crash, but the handler can be overriden. |
| 26 // saturated_cast<> - Analogous to static_cast<> for numeric types, except |
| 27 // that it returns a saturated result when the specified numeric conversion |
| 28 // would otherwise overflow or underflow. An NaN source returns 0 by |
| 29 // default, but can be overridden to return a different result. |
| 30 // strict_cast<> - Analogous to static_cast<> for numeric types, except that |
| 31 // it will cause a compile failure if the destination type is not large |
| 32 // enough to contain any value in the source type. It performs no runtime |
| 33 // checking and thus introduces no runtime overhead. |
| 34 // IsValueInRangeForNumericType<>() - A convenience function that returns true |
| 35 // if the type supplied to the template parameter can represent the value |
| 36 // passed as an argument to the function. |
| 37 // IsValueNegative<>() - A convenience function that will accept any arithmetic |
| 38 // type as an argument and will return whether the value is less than zero. |
| 39 // Unsigned types always return false. |
| 40 // StrictNumeric<> - A wrapper type that performs assignments and copies via |
| 41 // the strict_cast<> template, and can perform valid arithmetic comparisons |
| 42 // across any range of arithmetic types. StrictNumeric is the return type |
| 43 // for values extracted from a CheckedNumeric class instance. The raw |
| 44 // arithmetic value is extracted via static_cast to the underlying type. |
| 45 |
18 // Convenience function that returns true if the supplied value is in range | 46 // Convenience function that returns true if the supplied value is in range |
19 // for the destination type. | 47 // for the destination type. |
20 template <typename Dst, typename Src> | 48 template <typename Dst, typename Src> |
21 constexpr bool IsValueInRangeForNumericType(Src value) { | 49 constexpr bool IsValueInRangeForNumericType(Src value) { |
22 return internal::DstRangeRelationToSrcRange<Dst>(value) == | 50 return internal::DstRangeRelationToSrcRange<Dst>(value) == |
23 internal::RANGE_VALID; | 51 internal::RANGE_VALID; |
24 } | 52 } |
25 | 53 |
26 // Convenience function for determining if a numeric value is negative without | 54 // Convenience function for determining if a numeric value is negative without |
27 // throwing compiler warnings on: unsigned(value) < 0. | 55 // throwing compiler warnings on: unsigned(value) < 0. |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
84 constexpr Dst saturated_cast_impl(const Src value, | 112 constexpr Dst saturated_cast_impl(const Src value, |
85 const RangeConstraint constraint) { | 113 const RangeConstraint constraint) { |
86 return constraint == RANGE_VALID | 114 return constraint == RANGE_VALID |
87 ? static_cast<Dst>(value) | 115 ? static_cast<Dst>(value) |
88 : (constraint == RANGE_UNDERFLOW | 116 : (constraint == RANGE_UNDERFLOW |
89 ? std::numeric_limits<Dst>::min() | 117 ? std::numeric_limits<Dst>::min() |
90 : (constraint == RANGE_OVERFLOW | 118 : (constraint == RANGE_OVERFLOW |
91 ? std::numeric_limits<Dst>::max() | 119 ? std::numeric_limits<Dst>::max() |
92 : NaNHandler::template HandleFailure<Dst>())); | 120 : NaNHandler::template HandleFailure<Dst>())); |
93 } | 121 } |
94 } // namespace internal | |
95 | 122 |
96 // saturated_cast<> is analogous to static_cast<> for numeric types, except | 123 // saturated_cast<> is analogous to static_cast<> for numeric types, except |
97 // that the specified numeric conversion will saturate rather than overflow or | 124 // that the specified numeric conversion will saturate rather than overflow or |
98 // underflow. NaN assignment to an integral will defer the behavior to a | 125 // underflow. NaN assignment to an integral will defer the behavior to a |
99 // specified class. By default, it will return 0. | 126 // specified class. By default, it will return 0. |
100 template <typename Dst, | 127 template <typename Dst, |
101 class NaNHandler = SaturatedCastNaNBehaviorReturnZero, | 128 class NaNHandler = SaturatedCastNaNBehaviorReturnZero, |
102 typename Src> | 129 typename Src> |
103 constexpr Dst saturated_cast(Src value) { | 130 constexpr Dst saturated_cast(Src value) { |
104 return std::numeric_limits<Dst>::is_iec559 | 131 return std::numeric_limits<Dst>::is_iec559 |
105 ? static_cast<Dst>(value) // Floating point optimization. | 132 ? static_cast<Dst>(value) // Floating point optimization. |
106 : internal::saturated_cast_impl<Dst, NaNHandler>( | 133 : internal::saturated_cast_impl<Dst, NaNHandler>( |
107 value, internal::DstRangeRelationToSrcRange<Dst>(value)); | 134 value, internal::DstRangeRelationToSrcRange<Dst>(value)); |
108 } | 135 } |
109 | 136 |
110 // 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 |
111 // 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 |
112 // 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. |
113 template <typename Dst, typename Src> | 140 template <typename Dst, typename Src> |
114 constexpr Dst strict_cast(Src value) { | 141 constexpr Dst strict_cast(Src value) { |
115 static_assert(std::numeric_limits<Src>::is_specialized, | 142 static_assert(std::numeric_limits<Src>::is_specialized, |
116 "Argument must be numeric."); | 143 "Argument must be numeric."); |
117 static_assert(std::numeric_limits<Dst>::is_specialized, | 144 static_assert(std::numeric_limits<Dst>::is_specialized, |
118 "Result must be numeric."); | 145 "Result must be numeric."); |
119 static_assert((internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == | 146 |
120 internal::NUMERIC_RANGE_CONTAINED), | 147 // If you got here from a compiler error, it's because you tried to assign |
121 "The numeric conversion is out of range for this type. You " | 148 // from a source type to a destination type that has insufficient range. |
122 "should probably use one of the following conversion " | 149 // The solution may be to change the destination type you're assigning to, |
123 "mechanisms on the value you want to pass:\n" | 150 // and use one large enough to represent the source. |
124 "- base::checked_cast\n" | 151 // If you're assigning from a CheckedNumeric<> class, you can use the |
125 "- base::saturated_cast\n" | 152 // AssignIfValid() member function, specify a narrower destination type to the |
126 "- base::CheckedNumeric"); | 153 // member value functions (e.g. val.template ValueOrDie<Dst>()), or use one of |
| 154 // the value helper functions (e.g. ValueOrDieForType<Dst>(val)). |
| 155 // Alternatively, you may be better served with the checked_cast<> or |
| 156 // saturated_cast<> template functions for your particular use case. |
| 157 static_assert(internal::StaticDstRangeRelationToSrcRange<Dst, Src>::value == |
| 158 internal::NUMERIC_RANGE_CONTAINED, |
| 159 "The source type is out of range for the destination type. " |
| 160 "Please see strict_cast<> comments for more information."); |
127 | 161 |
128 return static_cast<Dst>(value); | 162 return static_cast<Dst>(value); |
129 } | 163 } |
130 | 164 |
131 // StrictNumeric implements compile time range checking between numeric types by | 165 // StrictNumeric implements compile time range checking between numeric types by |
132 // wrapping assignment operations in a strict_cast. This class is intended to be | 166 // wrapping assignment operations in a strict_cast. This class is intended to be |
133 // used for function arguments and return types, to ensure the destination type | 167 // used for function arguments and return types, to ensure the destination type |
134 // can always contain the source type. This is essentially the same as enforcing | 168 // can always contain the source type. This is essentially the same as enforcing |
135 // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied | 169 // -Wconversion in gcc and C4302 warnings on MSVC, but it can be applied |
136 // incrementally at API boundaries, making it easier to convert code so that it | 170 // incrementally at API boundaries, making it easier to convert code so that it |
(...skipping 13 matching lines...) Expand all Loading... |
150 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) | 184 constexpr StrictNumeric(const StrictNumeric<Src>& rhs) |
151 : value_(strict_cast<T>(rhs.value_)) {} | 185 : value_(strict_cast<T>(rhs.value_)) {} |
152 | 186 |
153 // This is not an explicit constructor because we implicitly upgrade regular | 187 // This is not an explicit constructor because we implicitly upgrade regular |
154 // numerics to StrictNumerics to make them easier to use. | 188 // numerics to StrictNumerics to make them easier to use. |
155 template <typename Src> | 189 template <typename Src> |
156 constexpr StrictNumeric(Src value) | 190 constexpr StrictNumeric(Src value) |
157 : value_(strict_cast<T>(value)) {} | 191 : value_(strict_cast<T>(value)) {} |
158 | 192 |
159 // The numeric cast operator basically handles all the magic. | 193 // The numeric cast operator basically handles all the magic. |
160 template <typename Dst> | 194 template <typename Dst, |
| 195 typename std::enable_if< |
| 196 ArithmeticOrUnderlyingEnum<Dst>::value>::type* = nullptr> |
161 constexpr operator Dst() const { | 197 constexpr operator Dst() const { |
162 return strict_cast<Dst>(value_); | 198 return strict_cast<typename ArithmeticOrUnderlyingEnum<Dst>::type>(value_); |
163 } | 199 } |
164 | 200 |
165 private: | 201 private: |
166 const T value_; | 202 const T value_; |
167 }; | 203 }; |
168 | 204 |
| 205 // Overload the ostream output operator to make logging work nicely. |
| 206 template <typename T> |
| 207 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { |
| 208 os << static_cast<T>(value); |
| 209 return os; |
| 210 } |
| 211 |
| 212 #define STRICT_COMPARISON_OP(NAME, OP) \ |
| 213 template <typename L, typename R, \ |
| 214 typename std::enable_if< \ |
| 215 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ |
| 216 constexpr bool operator OP(const L lhs, const R rhs) { \ |
| 217 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ |
| 218 typename UnderlyingType<R>::type>(lhs, rhs); \ |
| 219 } |
| 220 |
| 221 STRICT_COMPARISON_OP(IsLess, <); |
| 222 STRICT_COMPARISON_OP(IsLessOrEqual, <=); |
| 223 STRICT_COMPARISON_OP(IsGreater, >); |
| 224 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); |
| 225 STRICT_COMPARISON_OP(IsEqual, ==); |
| 226 STRICT_COMPARISON_OP(IsNotEqual, !=); |
| 227 |
| 228 #undef STRICT_COMPARISON_OP |
| 229 }; |
| 230 |
| 231 using internal::strict_cast; |
| 232 using internal::saturated_cast; |
| 233 using internal::StrictNumeric; |
| 234 |
169 // Explicitly make a shorter size_t typedef for convenience. | 235 // Explicitly make a shorter size_t typedef for convenience. |
170 typedef StrictNumeric<size_t> SizeT; | 236 typedef StrictNumeric<size_t> SizeT; |
171 | 237 |
172 } // namespace base | 238 } // namespace base |
173 | 239 |
174 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 240 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
OLD | NEW |