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> |
| 11 #include <ostream> | 11 #include <ostream> |
| 12 #include <type_traits> | 12 #include <type_traits> |
| 13 | 13 |
| 14 #include "base/numerics/safe_conversions_impl.h" | 14 #include "base/numerics/safe_conversions_impl.h" |
| 15 | 15 |
| 16 namespace base { | 16 namespace base { |
| 17 | 17 |
| 18 // The following are helper constexpr template functions and classes for safely | |
| 19 // performing a range of conversions, assignments, and tests: | |
| 20 // | |
| 21 // checked_cast<> - Analogous to static_cast<> for numeric types, except | |
| 22 // that it CHECKs that the specified numeric conversion will not overflow | |
| 23 // or underflow. NaN source will always trigger a CHECK. | |
| 24 // The default CHECK triggers a crash, but the handler can be overriden. | |
| 25 // saturated_cast<> - Analogous to static_cast<> for numeric types, except | |
| 26 // that it returns a saturated result when the specified numeric conversion | |
| 27 // would otherwise overflow or underflow. An NaN source returns 0 by | |
| 28 // default, but can be overridden to return a different result. | |
| 29 // strict_cast<> - Analogous to static_cast<> for numeric types, except that | |
| 30 // it will cause a compile failure if the destination type is not large | |
| 31 // enough to contain any value in the source type. It performs no runtime | |
| 32 // checking and thus introduces no runtime overhead. | |
| 33 // IsValueInRangeForNumericType<>() - A convenience function that returns true | |
| 34 // if the type supplied to the template parameter can represent the value | |
| 35 // passed as an argument to the function. | |
| 36 // IsValueNegative<>() - A convenience function that will accept any arithmetic | |
| 37 // type as an argument and will return whether the value is less than zero. | |
| 38 // Unsigned types always return false. | |
| 39 // SafeUnsignedAbs() - Returns the absolute value of the supplied integer | |
| 40 // parameter as an unsigned result (thus avoiding an overflow if the value | |
| 41 // is the signed, two's complement minimum). | |
| 42 // StrictNumeric<> - A wrapper type that performs assignments and copies via | |
| 43 // the strict_cast<> template, and can perform valid arithmetic comparisons | |
| 44 // across any range of arithmetic types. StrictNumeric is the return type | |
| 45 // for values extracted from a CheckedNumeric class instance. The raw | |
| 46 // arithmetic value is extracted via static_cast to the underlying type. | |
| 47 // MakeStrictNum() - Creates a new StrictNumeric from the underlying type of | |
| 48 // the supplied arithmetic or StrictNumeric type. | |
| 49 | |
| 50 // Convenience function that returns true if the supplied value is in range | 18 // Convenience function that returns true if the supplied value is in range |
| 51 // for the destination type. | 19 // for the destination type. |
| 52 template <typename Dst, typename Src> | 20 template <typename Dst, typename Src> |
| 53 constexpr bool IsValueInRangeForNumericType(Src value) { | 21 constexpr bool IsValueInRangeForNumericType(Src value) { |
| 54 return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid(); | 22 return internal::DstRangeRelationToSrcRange<Dst>(value).IsValid(); |
| 55 } | 23 } |
| 56 | 24 |
| 57 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. | 25 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. |
| 58 struct CheckOnFailure { | 26 struct CheckOnFailure { |
| 59 template <typename T> | 27 template <typename T> |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 75 typename Src> | 43 typename Src> |
| 76 constexpr Dst checked_cast(Src value) { | 44 constexpr Dst checked_cast(Src value) { |
| 77 // This throws a compile-time error on evaluating the constexpr if it can be | 45 // This throws a compile-time error on evaluating the constexpr if it can be |
| 78 // determined at compile-time as failing, otherwise it will CHECK at runtime. | 46 // determined at compile-time as failing, otherwise it will CHECK at runtime. |
| 79 using SrcType = typename internal::UnderlyingType<Src>::type; | 47 using SrcType = typename internal::UnderlyingType<Src>::type; |
| 80 return IsValueInRangeForNumericType<Dst, SrcType>(value) | 48 return IsValueInRangeForNumericType<Dst, SrcType>(value) |
| 81 ? static_cast<Dst>(static_cast<SrcType>(value)) | 49 ? static_cast<Dst>(static_cast<SrcType>(value)) |
| 82 : CheckHandler::template HandleFailure<Dst>(); | 50 : CheckHandler::template HandleFailure<Dst>(); |
| 83 } | 51 } |
| 84 | 52 |
| 53 // as_signed<> returns the supplied integral value (or integral castable | |
| 54 // Numeric template) cast as a signed integral of equivalent precision. | |
| 55 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) | |
| 56 template <typename Src> | |
| 57 constexpr typename std::make_signed< | |
| 58 typename base::internal::UnderlyingType<Src>::type>::type | |
| 59 as_signed(const Src value) { | |
| 60 static_assert(std::is_integral<decltype(as_signed(value))>::value, | |
| 61 "Argument must be a signed or unsigned integer type."); | |
| 62 return static_cast<decltype(as_signed(value))>(value); | |
| 63 } | |
| 64 | |
| 65 // as_unsigned<> returns the supplied integral value (or integral castable | |
| 66 // Numeric template) cast as an unsigned integral of equivalent precision. | |
| 67 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) | |
| 68 template <typename Src> | |
| 69 constexpr typename std::make_unsigned< | |
| 70 typename base::internal::UnderlyingType<Src>::type>::type | |
| 71 as_unsigned(const Src value) { | |
| 72 static_assert(std::is_integral<decltype(as_unsigned(value))>::value, | |
| 73 "Argument must be a signed or unsigned integer type."); | |
| 74 return static_cast<decltype(as_unsigned(value))>(value); | |
| 75 } | |
| 76 | |
| 85 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. | 77 // Default boundaries for integral/float: max/infinity, lowest/-infinity, 0/NaN. |
| 86 template <typename T> | 78 template <typename T> |
| 87 struct SaturationDefaultHandler { | 79 struct SaturationDefaultLimits : public std::numeric_limits<T> { |
| 88 static constexpr T NaN() { | 80 static constexpr T NaN() { |
| 89 return std::numeric_limits<T>::has_quiet_NaN | 81 return std::numeric_limits<T>::has_quiet_NaN |
| 90 ? std::numeric_limits<T>::quiet_NaN() | 82 ? std::numeric_limits<T>::quiet_NaN() |
| 91 : T(); | 83 : T(); |
| 92 } | 84 } |
| 93 static constexpr T max() { return std::numeric_limits<T>::max(); } | 85 // static constexpr T required() is required by the library. |
|
dcheng
2017/06/28 07:25:06
required() => max()?
Also I don't quite understan
jschuh
2017/06/28 12:32:38
D'oh! Typo. But the gist is that you can provide a
| |
| 94 static constexpr T Overflow() { | 86 static constexpr T Overflow() { |
| 95 return std::numeric_limits<T>::has_infinity | 87 return std::numeric_limits<T>::has_infinity |
| 96 ? std::numeric_limits<T>::infinity() | 88 ? std::numeric_limits<T>::infinity() |
| 97 : std::numeric_limits<T>::max(); | 89 : std::numeric_limits<T>::max(); |
| 98 } | 90 } |
| 99 static constexpr T lowest() { return std::numeric_limits<T>::lowest(); } | 91 // static constexpr T lowest() is required by the library. |
| 100 static constexpr T Underflow() { | 92 static constexpr T Underflow() { |
| 101 return std::numeric_limits<T>::has_infinity | 93 return std::numeric_limits<T>::has_infinity |
| 102 ? std::numeric_limits<T>::infinity() * -1 | 94 ? std::numeric_limits<T>::infinity() * -1 |
| 103 : std::numeric_limits<T>::lowest(); | 95 : std::numeric_limits<T>::lowest(); |
| 104 } | 96 } |
| 105 }; | 97 }; |
| 106 | 98 |
| 107 namespace internal { | 99 namespace internal { |
| 108 | 100 |
| 109 template <typename Dst, template <typename> class S, typename Src> | 101 template <typename Dst, template <typename> class S, typename Src> |
| 110 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { | 102 constexpr Dst saturated_cast_impl(Src value, RangeCheck constraint) { |
| 111 // For some reason clang generates much better code when the branch is | 103 // For some reason clang generates much better code when the branch is |
| 112 // structured exactly this way, rather than a sequence of checks. | 104 // structured exactly this way, rather than a sequence of checks. |
| 113 return !constraint.IsOverflowFlagSet() | 105 return !constraint.IsOverflowFlagSet() |
| 114 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) | 106 ? (!constraint.IsUnderflowFlagSet() ? static_cast<Dst>(value) |
| 115 : S<Dst>::Underflow()) | 107 : S<Dst>::Underflow()) |
| 116 // Skip this check for integral Src, which cannot be NaN. | 108 // Skip this check for integral Src, which cannot be NaN. |
| 117 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() | 109 : (std::is_integral<Src>::value || !constraint.IsUnderflowFlagSet() |
| 118 ? S<Dst>::Overflow() | 110 ? S<Dst>::Overflow() |
| 119 : S<Dst>::NaN()); | 111 : S<Dst>::NaN()); |
| 120 } | 112 } |
| 121 | 113 |
| 122 // saturated_cast<> is analogous to static_cast<> for numeric types, except | 114 // saturated_cast<> is analogous to static_cast<> for numeric types, except |
| 123 // that the specified numeric conversion will saturate by default rather than | 115 // that the specified numeric conversion will saturate by default rather than |
| 124 // overflow or underflow, and NaN assignment to an integral will return 0. | 116 // overflow or underflow, and NaN assignment to an integral will return 0. |
| 125 // All boundary condition behaviors can be overriden with a custom handler. | 117 // All boundary condition behaviors can be overriden with a custom handler. |
| 126 template <typename Dst, | 118 template <typename Dst, |
| 127 template <typename> | 119 template <typename> class SaturationHandler = SaturationDefaultLimits, |
| 128 class SaturationHandler = SaturationDefaultHandler, | |
| 129 typename Src> | 120 typename Src> |
| 130 constexpr Dst saturated_cast(Src value) { | 121 constexpr Dst saturated_cast(Src value) { |
| 131 using SrcType = typename UnderlyingType<Src>::type; | 122 using SrcType = typename UnderlyingType<Src>::type; |
| 132 return saturated_cast_impl<Dst, SaturationHandler, SrcType>( | 123 return saturated_cast_impl<Dst, SaturationHandler, SrcType>( |
| 133 value, | 124 value, |
| 134 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); | 125 DstRangeRelationToSrcRange<Dst, SaturationHandler, SrcType>(value)); |
| 135 } | 126 } |
| 136 | 127 |
| 137 // strict_cast<> is analogous to static_cast<> for numeric types, except that | 128 // strict_cast<> is analogous to static_cast<> for numeric types, except that |
| 138 // it will cause a compile failure if the destination type is not large enough | 129 // it will cause a compile failure if the destination type is not large enough |
| (...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 231 return value; | 222 return value; |
| 232 } | 223 } |
| 233 | 224 |
| 234 // Overload the ostream output operator to make logging work nicely. | 225 // Overload the ostream output operator to make logging work nicely. |
| 235 template <typename T> | 226 template <typename T> |
| 236 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { | 227 std::ostream& operator<<(std::ostream& os, const StrictNumeric<T>& value) { |
| 237 os << static_cast<T>(value); | 228 os << static_cast<T>(value); |
| 238 return os; | 229 return os; |
| 239 } | 230 } |
| 240 | 231 |
| 241 #define STRICT_COMPARISON_OP(NAME, OP) \ | 232 #define BASE_NUMERIC_COMPARISON_OPERATORS(CLASS, NAME, OP) \ |
|
jschuh
2017/06/26 20:26:50
Generalized the implementation so ClampedNumeric c
| |
| 242 template <typename L, typename R, \ | 233 template <typename L, typename R, \ |
| 243 typename std::enable_if< \ | 234 typename std::enable_if< \ |
| 244 internal::IsStrictOp<L, R>::value>::type* = nullptr> \ | 235 internal::Is##CLASS##Op<L, R>::value>::type* = nullptr> \ |
| 245 constexpr bool operator OP(const L lhs, const R rhs) { \ | 236 constexpr bool operator OP(const L lhs, const R rhs) { \ |
| 246 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ | 237 return SafeCompare<NAME, typename UnderlyingType<L>::type, \ |
| 247 typename UnderlyingType<R>::type>(lhs, rhs); \ | 238 typename UnderlyingType<R>::type>(lhs, rhs); \ |
| 248 } | 239 } |
| 249 | 240 |
| 250 STRICT_COMPARISON_OP(IsLess, <); | 241 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLess, <); |
| 251 STRICT_COMPARISON_OP(IsLessOrEqual, <=); | 242 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsLessOrEqual, <=); |
| 252 STRICT_COMPARISON_OP(IsGreater, >); | 243 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreater, >); |
| 253 STRICT_COMPARISON_OP(IsGreaterOrEqual, >=); | 244 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsGreaterOrEqual, >=); |
| 254 STRICT_COMPARISON_OP(IsEqual, ==); | 245 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsEqual, ==); |
| 255 STRICT_COMPARISON_OP(IsNotEqual, !=); | 246 BASE_NUMERIC_COMPARISON_OPERATORS(Strict, IsNotEqual, !=); |
| 256 | 247 |
| 257 #undef STRICT_COMPARISON_OP | 248 }; // namespace internal |
| 258 }; | |
| 259 | 249 |
| 260 using internal::strict_cast; | 250 using internal::strict_cast; |
| 261 using internal::saturated_cast; | 251 using internal::saturated_cast; |
| 262 using internal::SafeUnsignedAbs; | 252 using internal::SafeUnsignedAbs; |
| 263 using internal::StrictNumeric; | 253 using internal::StrictNumeric; |
| 264 using internal::MakeStrictNum; | 254 using internal::MakeStrictNum; |
| 265 using internal::IsValueNegative; | 255 using internal::IsValueNegative; |
| 266 | 256 |
| 267 // Explicitly make a shorter size_t alias for convenience. | 257 // Explicitly make a shorter size_t alias for convenience. |
| 268 using SizeT = StrictNumeric<size_t>; | 258 using SizeT = StrictNumeric<size_t>; |
| 269 | 259 |
| 270 } // namespace base | 260 } // namespace base |
| 271 | 261 |
| 272 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ | 262 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_H_ |
| OLD | NEW |