[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 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 |
| 3 | // found in the LICENSE file. |
| 4 | |
danakj | 0a44860 | 2015-03-10 00:31:16 | [diff] [blame] | 5 | #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
| 6 | #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 7 | |
avi | 9b6f4293 | 2015-12-26 22:15:14 | [diff] [blame] | 8 | #include <stdint.h> |
| 9 | |
rickyz | d2fe3e24 | 2016-05-10 13:47:37 | [diff] [blame] | 10 | #include <limits> |
thomasanderson | 61500f2 | 2016-08-31 01:30:37 | [diff] [blame] | 11 | #include <type_traits> |
rickyz | d2fe3e24 | 2016-05-10 13:47:37 | [diff] [blame] | 12 | |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 13 | namespace base { |
| 14 | namespace internal { |
| 15 | |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 16 | // The std library doesn't provide a binary max_exponent for integers, however |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 17 | // we can compute an analog using std::numeric_limits<>::digits. |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 18 | template <typename NumericType> |
| 19 | struct MaxExponent { |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 20 | static const int value = std::is_floating_point<NumericType>::value |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 21 | ? std::numeric_limits<NumericType>::max_exponent |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 22 | : std::numeric_limits<NumericType>::digits + 1; |
| 23 | }; |
| 24 | |
| 25 | // The number of bits (including the sign) in an integer. Eliminates sizeof |
| 26 | // hacks. |
| 27 | template <typename NumericType> |
| 28 | struct IntegerBitsPlusSign { |
| 29 | static const int value = std::numeric_limits<NumericType>::digits + |
| 30 | std::is_signed<NumericType>::value; |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 31 | }; |
| 32 | |
jschuh | ba3c4f94 | 2016-12-10 14:03:55 | [diff] [blame] | 33 | // Helper templates for integer manipulations. |
| 34 | |
| 35 | template <typename Integer> |
| 36 | struct PositionOfSignBit { |
| 37 | static const size_t value = IntegerBitsPlusSign<Integer>::value - 1; |
| 38 | }; |
| 39 | |
| 40 | template <typename T> |
| 41 | constexpr bool HasSignBit(T x) { |
| 42 | // Cast to unsigned since right shift on signed is undefined. |
| 43 | return !!(static_cast<typename std::make_unsigned<T>::type>(x) >> |
| 44 | PositionOfSignBit<T>::value); |
| 45 | } |
| 46 | |
| 47 | // This wrapper undoes the standard integer promotions. |
| 48 | template <typename T> |
| 49 | constexpr T BinaryComplement(T x) { |
| 50 | return static_cast<T>(~x); |
| 51 | } |
| 52 | |
jschuh | 71b669a | 2016-12-17 01:13:31 | [diff] [blame] | 53 | // Determines if a numeric value is negative without throwing compiler |
| 54 | // warnings on: unsigned(value) < 0. |
| 55 | template <typename T, |
| 56 | typename std::enable_if<std::is_signed<T>::value>::type* = nullptr> |
| 57 | constexpr bool IsValueNegative(T value) { |
| 58 | static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); |
| 59 | return value < 0; |
| 60 | } |
| 61 | |
| 62 | template <typename T, |
| 63 | typename std::enable_if<!std::is_signed<T>::value>::type* = nullptr> |
| 64 | constexpr bool IsValueNegative(T) { |
| 65 | static_assert(std::is_arithmetic<T>::value, "Argument must be numeric."); |
| 66 | return false; |
| 67 | } |
| 68 | |
| 69 | // This performs a fast negation, returning a signed value. It works on unsigned |
| 70 | // arguments, but probably doesn't do what you want for any unsigned value |
| 71 | // larger than max / 2 + 1 (i.e. signed min cast to unsigned). |
| 72 | template <typename T> |
| 73 | constexpr typename std::make_signed<T>::type ConditionalNegate( |
| 74 | T x, |
| 75 | bool is_negative) { |
| 76 | static_assert(std::is_integral<T>::value, "Type must be integral"); |
| 77 | using SignedT = typename std::make_signed<T>::type; |
| 78 | using UnsignedT = typename std::make_unsigned<T>::type; |
| 79 | return static_cast<SignedT>( |
| 80 | (static_cast<UnsignedT>(x) ^ -SignedT(is_negative)) + is_negative); |
| 81 | } |
| 82 | |
jschuh | ba3c4f94 | 2016-12-10 14:03:55 | [diff] [blame] | 83 | // This performs a safe, non-branching absolute value via unsigned overflow. |
| 84 | template <typename T> |
| 85 | constexpr T SafeUnsignedAbsImpl(T value, T sign_mask) { |
| 86 | static_assert(!std::is_signed<T>::value, "Types must be unsigned."); |
jschuh | e2d3dd9 | 2016-12-13 02:41:13 | [diff] [blame] | 87 | return (value ^ sign_mask) - sign_mask; |
jschuh | ba3c4f94 | 2016-12-10 14:03:55 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | template <typename T, |
| 91 | typename std::enable_if<std::is_integral<T>::value && |
| 92 | std::is_signed<T>::value>::type* = nullptr> |
| 93 | constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { |
| 94 | using UnsignedT = typename std::make_unsigned<T>::type; |
| 95 | return SafeUnsignedAbsImpl( |
| 96 | static_cast<UnsignedT>(value), |
| 97 | // The sign mask is all ones for negative and zero otherwise. |
| 98 | static_cast<UnsignedT>(-static_cast<T>(HasSignBit(value)))); |
| 99 | } |
| 100 | |
| 101 | template <typename T, |
| 102 | typename std::enable_if<std::is_integral<T>::value && |
| 103 | !std::is_signed<T>::value>::type* = nullptr> |
| 104 | constexpr T SafeUnsignedAbs(T value) { |
| 105 | // T is unsigned, so |value| must already be positive. |
| 106 | return static_cast<T>(value); |
| 107 | } |
| 108 | |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 109 | enum IntegerRepresentation { |
| 110 | INTEGER_REPRESENTATION_UNSIGNED, |
| 111 | INTEGER_REPRESENTATION_SIGNED |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 112 | }; |
| 113 | |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 114 | // A range for a given nunmeric Src type is contained for a given numeric Dst |
| 115 | // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 116 | // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 117 | // We implement this as template specializations rather than simple static |
| 118 | // comparisons to ensure type correctness in our comparisons. |
| 119 | enum NumericRangeRepresentation { |
| 120 | NUMERIC_RANGE_NOT_CONTAINED, |
| 121 | NUMERIC_RANGE_CONTAINED |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 122 | }; |
| 123 | |
| 124 | // Helper templates to statically determine if our destination type can contain |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 125 | // maximum and minimum values represented by the source type. |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 126 | |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 127 | template <typename Dst, |
| 128 | typename Src, |
| 129 | IntegerRepresentation DstSign = std::is_signed<Dst>::value |
| 130 | ? INTEGER_REPRESENTATION_SIGNED |
| 131 | : INTEGER_REPRESENTATION_UNSIGNED, |
| 132 | IntegerRepresentation SrcSign = std::is_signed<Src>::value |
| 133 | ? INTEGER_REPRESENTATION_SIGNED |
| 134 | : INTEGER_REPRESENTATION_UNSIGNED> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 135 | struct StaticDstRangeRelationToSrcRange; |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 136 | |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 137 | // Same sign: Dst is guaranteed to contain Src only if its range is equal or |
| 138 | // larger. |
| 139 | template <typename Dst, typename Src, IntegerRepresentation Sign> |
| 140 | struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { |
| 141 | static const NumericRangeRepresentation value = |
| 142 | MaxExponent<Dst>::value >= MaxExponent<Src>::value |
| 143 | ? NUMERIC_RANGE_CONTAINED |
| 144 | : NUMERIC_RANGE_NOT_CONTAINED; |
| 145 | }; |
| 146 | |
| 147 | // Unsigned to signed: Dst is guaranteed to contain source only if its range is |
| 148 | // larger. |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 149 | template <typename Dst, typename Src> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 150 | struct StaticDstRangeRelationToSrcRange<Dst, |
| 151 | Src, |
| 152 | INTEGER_REPRESENTATION_SIGNED, |
| 153 | INTEGER_REPRESENTATION_UNSIGNED> { |
| 154 | static const NumericRangeRepresentation value = |
| 155 | MaxExponent<Dst>::value > MaxExponent<Src>::value |
| 156 | ? NUMERIC_RANGE_CONTAINED |
| 157 | : NUMERIC_RANGE_NOT_CONTAINED; |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 158 | }; |
| 159 | |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 160 | // Signed to unsigned: Dst cannot be statically determined to contain Src. |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 161 | template <typename Dst, typename Src> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 162 | struct StaticDstRangeRelationToSrcRange<Dst, |
| 163 | Src, |
| 164 | INTEGER_REPRESENTATION_UNSIGNED, |
| 165 | INTEGER_REPRESENTATION_SIGNED> { |
| 166 | static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 167 | }; |
| 168 | |
jschuh | 3e898ba | 2016-12-19 21:13:25 | [diff] [blame] | 169 | enum RangeConstraint { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 170 | RANGE_VALID = 0x0, // Value can be represented by the destination type. |
| 171 | RANGE_UNDERFLOW = 0x1, // Value would underflow. |
| 172 | RANGE_OVERFLOW = 0x2, // Value would overflow. |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 173 | RANGE_INVALID = RANGE_UNDERFLOW | RANGE_OVERFLOW // Invalid (i.e. NaN). |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 174 | }; |
| 175 | |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 176 | // This class wraps the range constraints as separate booleans so the compiler |
| 177 | // can identify constants and eliminate unused code paths. |
| 178 | class RangeCheck { |
| 179 | public: |
| 180 | constexpr RangeCheck(bool is_in_upper_bound, bool is_in_lower_bound) |
| 181 | : is_overflow_(!is_in_upper_bound), is_underflow_(!is_in_lower_bound) {} |
| 182 | constexpr RangeCheck() : is_overflow_(0), is_underflow_(0) {} |
| 183 | constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } |
| 184 | constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } |
| 185 | constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } |
| 186 | constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 187 | |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 188 | // These are some wrappers to make the tests a bit cleaner. |
| 189 | constexpr operator RangeConstraint() const { |
| 190 | return static_cast<RangeConstraint>(static_cast<int>(is_overflow_) << 1 | |
| 191 | static_cast<int>(is_underflow_)); |
| 192 | } |
| 193 | constexpr bool operator==(const RangeConstraint rhs) const { |
| 194 | return rhs == static_cast<RangeConstraint>(*this); |
| 195 | } |
| 196 | |
| 197 | private: |
| 198 | const bool is_overflow_; |
| 199 | const bool is_underflow_; |
| 200 | }; |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 201 | |
jschuh | fafe071 | 2015-09-14 20:21:24 | [diff] [blame] | 202 | // The following helper template addresses a corner case in range checks for |
| 203 | // conversion from a floating-point type to an integral type of smaller range |
| 204 | // but larger precision (e.g. float -> unsigned). The problem is as follows: |
| 205 | // 1. Integral maximum is always one less than a power of two, so it must be |
| 206 | // truncated to fit the mantissa of the floating point. The direction of |
| 207 | // rounding is implementation defined, but by default it's always IEEE |
| 208 | // floats, which round to nearest and thus result in a value of larger |
| 209 | // magnitude than the integral value. |
| 210 | // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX |
| 211 | // // is 4294967295u. |
| 212 | // 2. If the floating point value is equal to the promoted integral maximum |
| 213 | // value, a range check will erroneously pass. |
| 214 | // Example: (4294967296f <= 4294967295u) // This is true due to a precision |
| 215 | // // loss in rounding up to float. |
| 216 | // 3. When the floating point value is then converted to an integral, the |
| 217 | // resulting value is out of range for the target integral type and |
| 218 | // thus is implementation defined. |
| 219 | // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. |
| 220 | // To fix this bug we manually truncate the maximum value when the destination |
| 221 | // type is an integral of larger precision than the source floating-point type, |
| 222 | // such that the resulting maximum is represented exactly as a floating point. |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 223 | template <typename Dst, typename Src, template <typename> class Bounds> |
jschuh | fafe071 | 2015-09-14 20:21:24 | [diff] [blame] | 224 | struct NarrowingRange { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 225 | using SrcLimits = std::numeric_limits<Src>; |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 226 | using DstLimits = typename std::numeric_limits<Dst>; |
jschuh | fafe071 | 2015-09-14 20:21:24 | [diff] [blame] | 227 | |
jschuh | 71b669a | 2016-12-17 01:13:31 | [diff] [blame] | 228 | // Computes the mask required to make an accurate comparison between types. |
| 229 | static const int kShift = |
| 230 | (MaxExponent<Src>::value > MaxExponent<Dst>::value && |
| 231 | SrcLimits::digits < DstLimits::digits) |
| 232 | ? (DstLimits::digits - SrcLimits::digits) |
| 233 | : 0; |
| 234 | template < |
| 235 | typename T, |
| 236 | typename std::enable_if<std::is_integral<T>::value>::type* = nullptr> |
| 237 | |
| 238 | // Masks out the integer bits that are beyond the precision of the |
| 239 | // intermediate type used for comparison. |
| 240 | static constexpr T Adjust(T value) { |
| 241 | static_assert(std::is_same<T, Dst>::value, ""); |
| 242 | static_assert(kShift < DstLimits::digits, ""); |
| 243 | return static_cast<T>( |
| 244 | ConditionalNegate(SafeUnsignedAbs(value) & ~((T(1) << kShift) - T(1)), |
| 245 | IsValueNegative(value))); |
jschuh | fafe071 | 2015-09-14 20:21:24 | [diff] [blame] | 246 | } |
| 247 | |
jschuh | 71b669a | 2016-12-17 01:13:31 | [diff] [blame] | 248 | template <typename T, |
| 249 | typename std::enable_if<std::is_floating_point<T>::value>::type* = |
| 250 | nullptr> |
| 251 | static constexpr T Adjust(T value) { |
| 252 | static_assert(std::is_same<T, Dst>::value, ""); |
| 253 | static_assert(kShift == 0, ""); |
| 254 | return value; |
| 255 | } |
| 256 | |
| 257 | static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } |
| 258 | static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } |
jschuh | fafe071 | 2015-09-14 20:21:24 | [diff] [blame] | 259 | }; |
| 260 | |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 261 | template <typename Dst, |
| 262 | typename Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 263 | template <typename> class Bounds, |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 264 | IntegerRepresentation DstSign = std::is_signed<Dst>::value |
| 265 | ? INTEGER_REPRESENTATION_SIGNED |
| 266 | : INTEGER_REPRESENTATION_UNSIGNED, |
| 267 | IntegerRepresentation SrcSign = std::is_signed<Src>::value |
| 268 | ? INTEGER_REPRESENTATION_SIGNED |
| 269 | : INTEGER_REPRESENTATION_UNSIGNED, |
| 270 | NumericRangeRepresentation DstRange = |
| 271 | StaticDstRangeRelationToSrcRange<Dst, Src>::value> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 272 | struct DstRangeRelationToSrcRangeImpl; |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 273 | |
| 274 | // The following templates are for ranges that must be verified at runtime. We |
| 275 | // split it into checks based on signedness to avoid confusing casts and |
| 276 | // compiler warnings on signed an unsigned comparisons. |
| 277 | |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 278 | // Same sign narrowing: The range is contained for normal limits. |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 279 | template <typename Dst, |
| 280 | typename Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 281 | template <typename> class Bounds, |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 282 | IntegerRepresentation DstSign, |
| 283 | IntegerRepresentation SrcSign> |
| 284 | struct DstRangeRelationToSrcRangeImpl<Dst, |
| 285 | Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 286 | Bounds, |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 287 | DstSign, |
| 288 | SrcSign, |
| 289 | NUMERIC_RANGE_CONTAINED> { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 290 | static constexpr RangeCheck Check(Src value) { |
| 291 | using SrcLimits = std::numeric_limits<Src>; |
| 292 | using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 293 | return RangeCheck( |
| 294 | static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || |
| 295 | static_cast<Dst>(value) <= DstLimits::max(), |
| 296 | static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || |
| 297 | static_cast<Dst>(value) >= DstLimits::lowest()); |
| 298 | } |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 299 | }; |
| 300 | |
| 301 | // Signed to signed narrowing: Both the upper and lower boundaries may be |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 302 | // exceeded for standard limits. |
| 303 | template <typename Dst, typename Src, template <typename> class Bounds> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 304 | struct DstRangeRelationToSrcRangeImpl<Dst, |
| 305 | Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 306 | Bounds, |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 307 | INTEGER_REPRESENTATION_SIGNED, |
| 308 | INTEGER_REPRESENTATION_SIGNED, |
| 309 | NUMERIC_RANGE_NOT_CONTAINED> { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 310 | static constexpr RangeCheck Check(Src value) { |
| 311 | using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 312 | return RangeCheck(value <= DstLimits::max(), value >= DstLimits::lowest()); |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 313 | } |
| 314 | }; |
| 315 | |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 316 | // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for |
| 317 | // standard limits. |
| 318 | template <typename Dst, typename Src, template <typename> class Bounds> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 319 | struct DstRangeRelationToSrcRangeImpl<Dst, |
| 320 | Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 321 | Bounds, |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 322 | INTEGER_REPRESENTATION_UNSIGNED, |
| 323 | INTEGER_REPRESENTATION_UNSIGNED, |
| 324 | NUMERIC_RANGE_NOT_CONTAINED> { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 325 | static constexpr RangeCheck Check(Src value) { |
| 326 | using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 327 | return RangeCheck( |
| 328 | value <= DstLimits::max(), |
| 329 | DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest()); |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 330 | } |
| 331 | }; |
| 332 | |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 333 | // Unsigned to signed: Only the upper bound can be exceeded for standard limits. |
| 334 | template <typename Dst, typename Src, template <typename> class Bounds> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 335 | struct DstRangeRelationToSrcRangeImpl<Dst, |
| 336 | Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 337 | Bounds, |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 338 | INTEGER_REPRESENTATION_SIGNED, |
| 339 | INTEGER_REPRESENTATION_UNSIGNED, |
| 340 | NUMERIC_RANGE_NOT_CONTAINED> { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 341 | static constexpr RangeCheck Check(Src value) { |
| 342 | using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 343 | using Promotion = decltype(Src() + Dst()); |
| 344 | return RangeCheck(static_cast<Promotion>(value) <= |
| 345 | static_cast<Promotion>(DstLimits::max()), |
| 346 | DstLimits::lowest() <= Dst(0) || |
| 347 | static_cast<Promotion>(value) >= |
| 348 | static_cast<Promotion>(DstLimits::lowest())); |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 349 | } |
| 350 | }; |
| 351 | |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 352 | // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 353 | // and any negative value exceeds the lower boundary for standard limits. |
| 354 | template <typename Dst, typename Src, template <typename> class Bounds> |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 355 | struct DstRangeRelationToSrcRangeImpl<Dst, |
| 356 | Src, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 357 | Bounds, |
[email protected] | 5bfecbc5 | 2014-02-27 13:49:04 | [diff] [blame] | 358 | INTEGER_REPRESENTATION_UNSIGNED, |
| 359 | INTEGER_REPRESENTATION_SIGNED, |
| 360 | NUMERIC_RANGE_NOT_CONTAINED> { |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 361 | static constexpr RangeCheck Check(Src value) { |
| 362 | using SrcLimits = std::numeric_limits<Src>; |
| 363 | using DstLimits = NarrowingRange<Dst, Src, Bounds>; |
| 364 | using Promotion = decltype(Src() + Dst()); |
| 365 | return RangeCheck( |
| 366 | static_cast<Promotion>(SrcLimits::max()) <= |
| 367 | static_cast<Promotion>(DstLimits::max()) || |
| 368 | static_cast<Promotion>(value) <= |
| 369 | static_cast<Promotion>(DstLimits::max()), |
| 370 | value >= Src(0) && (DstLimits::lowest() == 0 || |
| 371 | static_cast<Dst>(value) >= DstLimits::lowest())); |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 372 | } |
| 373 | }; |
| 374 | |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 375 | template <typename Dst, |
| 376 | template <typename> class Bounds = std::numeric_limits, |
| 377 | typename Src> |
| 378 | constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 379 | static_assert(std::is_arithmetic<Src>::value, "Argument must be numeric."); |
| 380 | static_assert(std::is_arithmetic<Dst>::value, "Result must be numeric."); |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 381 | static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); |
| 382 | return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 383 | } |
| 384 | |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 385 | // Integer promotion templates used by the portable checked integer arithmetic. |
| 386 | template <size_t Size, bool IsSigned> |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 387 | struct IntegerForDigitsAndSign; |
| 388 | |
| 389 | #define INTEGER_FOR_DIGITS_AND_SIGN(I) \ |
| 390 | template <> \ |
| 391 | struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ |
| 392 | std::is_signed<I>::value> { \ |
| 393 | using type = I; \ |
| 394 | } |
| 395 | |
| 396 | INTEGER_FOR_DIGITS_AND_SIGN(int8_t); |
| 397 | INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); |
| 398 | INTEGER_FOR_DIGITS_AND_SIGN(int16_t); |
| 399 | INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); |
| 400 | INTEGER_FOR_DIGITS_AND_SIGN(int32_t); |
| 401 | INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); |
| 402 | INTEGER_FOR_DIGITS_AND_SIGN(int64_t); |
| 403 | INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); |
| 404 | #undef INTEGER_FOR_DIGITS_AND_SIGN |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 405 | |
| 406 | // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to |
| 407 | // support 128-bit math, then the ArithmeticPromotion template below will need |
| 408 | // to be updated (or more likely replaced with a decltype expression). |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 409 | static_assert(IntegerBitsPlusSign<intmax_t>::value == 64, |
| 410 | "Max integer size not supported for this toolchain."); |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 411 | |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 412 | template <typename Integer, bool IsSigned = std::is_signed<Integer>::value> |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 413 | struct TwiceWiderInteger { |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 414 | using type = |
| 415 | typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2, |
| 416 | IsSigned>::type; |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 417 | }; |
| 418 | |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 419 | enum ArithmeticPromotionCategory { |
jschuh | 466e984 | 2016-12-02 21:01:39 | [diff] [blame] | 420 | LEFT_PROMOTION, // Use the type of the left-hand argument. |
| 421 | RIGHT_PROMOTION // Use the type of the right-hand argument. |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 422 | }; |
| 423 | |
jschuh | 711ac6a | 2016-12-04 07:17:48 | [diff] [blame] | 424 | // Determines the type that can represent the largest positive value. |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 425 | template <typename Lhs, |
| 426 | typename Rhs, |
| 427 | ArithmeticPromotionCategory Promotion = |
| 428 | (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) |
| 429 | ? LEFT_PROMOTION |
| 430 | : RIGHT_PROMOTION> |
| 431 | struct MaxExponentPromotion; |
| 432 | |
| 433 | template <typename Lhs, typename Rhs> |
| 434 | struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { |
| 435 | using type = Lhs; |
| 436 | }; |
| 437 | |
| 438 | template <typename Lhs, typename Rhs> |
| 439 | struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { |
| 440 | using type = Rhs; |
| 441 | }; |
| 442 | |
jschuh | 711ac6a | 2016-12-04 07:17:48 | [diff] [blame] | 443 | // Determines the type that can represent the lowest arithmetic value. |
| 444 | template <typename Lhs, |
| 445 | typename Rhs, |
| 446 | ArithmeticPromotionCategory Promotion = |
| 447 | std::is_signed<Lhs>::value |
| 448 | ? (std::is_signed<Rhs>::value |
| 449 | ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value |
| 450 | ? LEFT_PROMOTION |
| 451 | : RIGHT_PROMOTION) |
| 452 | : LEFT_PROMOTION) |
| 453 | : (std::is_signed<Rhs>::value |
| 454 | ? RIGHT_PROMOTION |
| 455 | : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value |
| 456 | ? LEFT_PROMOTION |
| 457 | : RIGHT_PROMOTION))> |
| 458 | struct LowestValuePromotion; |
| 459 | |
| 460 | template <typename Lhs, typename Rhs> |
| 461 | struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> { |
| 462 | using type = Lhs; |
| 463 | }; |
| 464 | |
| 465 | template <typename Lhs, typename Rhs> |
| 466 | struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> { |
| 467 | using type = Rhs; |
| 468 | }; |
| 469 | |
| 470 | // Determines the type that is best able to represent an arithmetic result. |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 471 | template < |
| 472 | typename Lhs, |
| 473 | typename Rhs = Lhs, |
| 474 | bool is_intmax_type = |
| 475 | std::is_integral<typename MaxExponentPromotion<Lhs, Rhs>::type>::value&& |
| 476 | IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>:: |
| 477 | value == IntegerBitsPlusSign<intmax_t>::value, |
| 478 | bool is_max_exponent = |
| 479 | StaticDstRangeRelationToSrcRange< |
| 480 | typename MaxExponentPromotion<Lhs, Rhs>::type, |
| 481 | Lhs>::value == |
| 482 | NUMERIC_RANGE_CONTAINED&& StaticDstRangeRelationToSrcRange< |
| 483 | typename MaxExponentPromotion<Lhs, Rhs>::type, |
| 484 | Rhs>::value == NUMERIC_RANGE_CONTAINED> |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 485 | struct BigEnoughPromotion; |
| 486 | |
| 487 | // The side with the max exponent is big enough. |
| 488 | template <typename Lhs, typename Rhs, bool is_intmax_type> |
| 489 | struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { |
| 490 | using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
| 491 | static const bool is_contained = true; |
| 492 | }; |
| 493 | |
| 494 | // We can use a twice wider type to fit. |
| 495 | template <typename Lhs, typename Rhs> |
| 496 | struct BigEnoughPromotion<Lhs, Rhs, false, false> { |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 497 | using type = |
| 498 | typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, |
| 499 | std::is_signed<Lhs>::value || |
| 500 | std::is_signed<Rhs>::value>::type; |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 501 | static const bool is_contained = true; |
| 502 | }; |
| 503 | |
| 504 | // No type is large enough. |
| 505 | template <typename Lhs, typename Rhs> |
| 506 | struct BigEnoughPromotion<Lhs, Rhs, true, false> { |
| 507 | using type = typename MaxExponentPromotion<Lhs, Rhs>::type; |
| 508 | static const bool is_contained = false; |
| 509 | }; |
| 510 | |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 511 | // We can statically check if operations on the provided types can wrap, so we |
| 512 | // can skip the checked operations if they're not needed. So, for an integer we |
| 513 | // care if the destination type preserves the sign and is twice the width of |
| 514 | // the source. |
| 515 | template <typename T, typename Lhs, typename Rhs> |
| 516 | struct IsIntegerArithmeticSafe { |
jschuh | 5030b00c | 2016-12-05 18:21:48 | [diff] [blame] | 517 | static const bool value = |
| 518 | !std::is_floating_point<T>::value && |
| 519 | StaticDstRangeRelationToSrcRange<T, Lhs>::value == |
| 520 | NUMERIC_RANGE_CONTAINED && |
| 521 | IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) && |
| 522 | StaticDstRangeRelationToSrcRange<T, Rhs>::value != |
| 523 | NUMERIC_RANGE_CONTAINED && |
| 524 | IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value); |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 525 | }; |
| 526 | |
| 527 | // This hacks around libstdc++ 4.6 missing stuff in type_traits. |
| 528 | #if defined(__GLIBCXX__) |
| 529 | #define PRIV_GLIBCXX_4_7_0 20120322 |
| 530 | #define PRIV_GLIBCXX_4_5_4 20120702 |
| 531 | #define PRIV_GLIBCXX_4_6_4 20121127 |
| 532 | #if (__GLIBCXX__ < PRIV_GLIBCXX_4_7_0 || __GLIBCXX__ == PRIV_GLIBCXX_4_5_4 || \ |
| 533 | __GLIBCXX__ == PRIV_GLIBCXX_4_6_4) |
| 534 | #define PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX |
| 535 | #undef PRIV_GLIBCXX_4_7_0 |
| 536 | #undef PRIV_GLIBCXX_4_5_4 |
| 537 | #undef PRIV_GLIBCXX_4_6_4 |
| 538 | #endif |
| 539 | #endif |
| 540 | |
| 541 | // Extracts the underlying type from an enum. |
| 542 | template <typename T, bool is_enum = std::is_enum<T>::value> |
| 543 | struct ArithmeticOrUnderlyingEnum; |
| 544 | |
| 545 | template <typename T> |
| 546 | struct ArithmeticOrUnderlyingEnum<T, true> { |
| 547 | #if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX) |
| 548 | using type = __underlying_type(T); |
| 549 | #else |
| 550 | using type = typename std::underlying_type<T>::type; |
| 551 | #endif |
| 552 | static const bool value = std::is_arithmetic<type>::value; |
| 553 | }; |
| 554 | |
| 555 | #if defined(PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX) |
| 556 | #undef PRIV_USE_FALLBACKS_FOR_OLD_GLIBCXX |
| 557 | #endif |
| 558 | |
| 559 | template <typename T> |
| 560 | struct ArithmeticOrUnderlyingEnum<T, false> { |
| 561 | using type = T; |
| 562 | static const bool value = std::is_arithmetic<type>::value; |
| 563 | }; |
| 564 | |
| 565 | // The following are helper templates used in the CheckedNumeric class. |
| 566 | template <typename T> |
| 567 | class CheckedNumeric; |
| 568 | |
| 569 | template <typename T> |
| 570 | class StrictNumeric; |
| 571 | |
| 572 | // Used to treat CheckedNumeric and arithmetic underlying types the same. |
| 573 | template <typename T> |
| 574 | struct UnderlyingType { |
| 575 | using type = typename ArithmeticOrUnderlyingEnum<T>::type; |
| 576 | static const bool is_numeric = std::is_arithmetic<type>::value; |
| 577 | static const bool is_checked = false; |
| 578 | static const bool is_strict = false; |
| 579 | }; |
| 580 | |
| 581 | template <typename T> |
| 582 | struct UnderlyingType<CheckedNumeric<T>> { |
| 583 | using type = T; |
| 584 | static const bool is_numeric = true; |
| 585 | static const bool is_checked = true; |
| 586 | static const bool is_strict = false; |
| 587 | }; |
| 588 | |
| 589 | template <typename T> |
| 590 | struct UnderlyingType<StrictNumeric<T>> { |
| 591 | using type = T; |
| 592 | static const bool is_numeric = true; |
| 593 | static const bool is_checked = false; |
| 594 | static const bool is_strict = true; |
| 595 | }; |
| 596 | |
| 597 | template <typename L, typename R> |
| 598 | struct IsCheckedOp { |
| 599 | static const bool value = |
| 600 | UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
| 601 | (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); |
| 602 | }; |
| 603 | |
| 604 | template <typename L, typename R> |
| 605 | struct IsStrictOp { |
| 606 | static const bool value = |
| 607 | UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && |
| 608 | (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict); |
| 609 | }; |
| 610 | |
| 611 | template <typename L, typename R> |
| 612 | constexpr bool IsLessImpl(const L lhs, |
| 613 | const R rhs, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 614 | const RangeCheck l_range, |
| 615 | const RangeCheck r_range) { |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 616 | return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || |
| 617 | (l_range == r_range && |
| 618 | static_cast<decltype(lhs + rhs)>(lhs) < |
| 619 | static_cast<decltype(lhs + rhs)>(rhs)); |
| 620 | } |
| 621 | |
| 622 | template <typename L, typename R> |
| 623 | struct IsLess { |
| 624 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 625 | "Types must be numeric."); |
| 626 | static constexpr bool Test(const L lhs, const R rhs) { |
| 627 | return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 628 | DstRangeRelationToSrcRange<L>(rhs)); |
| 629 | } |
| 630 | }; |
| 631 | |
| 632 | template <typename L, typename R> |
| 633 | constexpr bool IsLessOrEqualImpl(const L lhs, |
| 634 | const R rhs, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 635 | const RangeCheck l_range, |
| 636 | const RangeCheck r_range) { |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 637 | return l_range == RANGE_UNDERFLOW || r_range == RANGE_OVERFLOW || |
| 638 | (l_range == r_range && |
| 639 | static_cast<decltype(lhs + rhs)>(lhs) <= |
| 640 | static_cast<decltype(lhs + rhs)>(rhs)); |
| 641 | } |
| 642 | |
| 643 | template <typename L, typename R> |
| 644 | struct IsLessOrEqual { |
| 645 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 646 | "Types must be numeric."); |
| 647 | static constexpr bool Test(const L lhs, const R rhs) { |
| 648 | return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 649 | DstRangeRelationToSrcRange<L>(rhs)); |
| 650 | } |
| 651 | }; |
| 652 | |
| 653 | template <typename L, typename R> |
| 654 | constexpr bool IsGreaterImpl(const L lhs, |
| 655 | const R rhs, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 656 | const RangeCheck l_range, |
| 657 | const RangeCheck r_range) { |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 658 | return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || |
| 659 | (l_range == r_range && |
| 660 | static_cast<decltype(lhs + rhs)>(lhs) > |
| 661 | static_cast<decltype(lhs + rhs)>(rhs)); |
| 662 | } |
| 663 | |
| 664 | template <typename L, typename R> |
| 665 | struct IsGreater { |
| 666 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 667 | "Types must be numeric."); |
| 668 | static constexpr bool Test(const L lhs, const R rhs) { |
| 669 | return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 670 | DstRangeRelationToSrcRange<L>(rhs)); |
| 671 | } |
| 672 | }; |
| 673 | |
| 674 | template <typename L, typename R> |
| 675 | constexpr bool IsGreaterOrEqualImpl(const L lhs, |
| 676 | const R rhs, |
jschuh | e3bd1f6 | 2016-12-20 05:11:30 | [diff] [blame^] | 677 | const RangeCheck l_range, |
| 678 | const RangeCheck r_range) { |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 679 | return l_range == RANGE_OVERFLOW || r_range == RANGE_UNDERFLOW || |
| 680 | (l_range == r_range && |
| 681 | static_cast<decltype(lhs + rhs)>(lhs) >= |
| 682 | static_cast<decltype(lhs + rhs)>(rhs)); |
| 683 | } |
| 684 | |
| 685 | template <typename L, typename R> |
| 686 | struct IsGreaterOrEqual { |
| 687 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 688 | "Types must be numeric."); |
| 689 | static constexpr bool Test(const L lhs, const R rhs) { |
| 690 | return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), |
| 691 | DstRangeRelationToSrcRange<L>(rhs)); |
| 692 | } |
| 693 | }; |
| 694 | |
| 695 | template <typename L, typename R> |
| 696 | struct IsEqual { |
| 697 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 698 | "Types must be numeric."); |
| 699 | static constexpr bool Test(const L lhs, const R rhs) { |
| 700 | return DstRangeRelationToSrcRange<R>(lhs) == |
| 701 | DstRangeRelationToSrcRange<L>(rhs) && |
| 702 | static_cast<decltype(lhs + rhs)>(lhs) == |
| 703 | static_cast<decltype(lhs + rhs)>(rhs); |
| 704 | } |
| 705 | }; |
| 706 | |
| 707 | template <typename L, typename R> |
| 708 | struct IsNotEqual { |
| 709 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 710 | "Types must be numeric."); |
| 711 | static constexpr bool Test(const L lhs, const R rhs) { |
| 712 | return DstRangeRelationToSrcRange<R>(lhs) != |
| 713 | DstRangeRelationToSrcRange<L>(rhs) || |
| 714 | static_cast<decltype(lhs + rhs)>(lhs) != |
| 715 | static_cast<decltype(lhs + rhs)>(rhs); |
| 716 | } |
| 717 | }; |
| 718 | |
| 719 | // These perform the actual math operations on the CheckedNumerics. |
| 720 | // Binary arithmetic operations. |
| 721 | template <template <typename, typename> class C, typename L, typename R> |
| 722 | constexpr bool SafeCompare(const L lhs, const R rhs) { |
| 723 | static_assert(std::is_arithmetic<L>::value && std::is_arithmetic<R>::value, |
| 724 | "Types must be numeric."); |
jschuh | 466e984 | 2016-12-02 21:01:39 | [diff] [blame] | 725 | using Promotion = BigEnoughPromotion<L, R>; |
jschuh | 23a4b06 | 2016-12-02 02:55:08 | [diff] [blame] | 726 | using BigType = typename Promotion::type; |
| 727 | return Promotion::is_contained |
| 728 | // Force to a larger type for speed if both are contained. |
| 729 | ? C<BigType, BigType>::Test( |
| 730 | static_cast<BigType>(static_cast<L>(lhs)), |
| 731 | static_cast<BigType>(static_cast<R>(rhs))) |
| 732 | // Let the template functions figure it out for mixed types. |
| 733 | : C<L, R>::Test(lhs, rhs); |
| 734 | }; |
| 735 | |
[email protected] | 4efb2c3 | 2014-01-16 06:57:25 | [diff] [blame] | 736 | } // namespace internal |
| 737 | } // namespace base |
| 738 | |
danakj | 0a44860 | 2015-03-10 00:31:16 | [diff] [blame] | 739 | #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |