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