safe_conversions_impl.h (34547B)
1 // Copyright 2014 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ 6 #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ 7 8 #include <stdint.h> 9 10 #include <limits> 11 #include <type_traits> 12 13 #include "base/compiler_specific.h" 14 15 #if defined(__GNUC__) || defined(__clang__) 16 #define BASE_NUMERICS_LIKELY(x) __builtin_expect(!!(x), 1) 17 #define BASE_NUMERICS_UNLIKELY(x) __builtin_expect(!!(x), 0) 18 #else 19 #define BASE_NUMERICS_LIKELY(x) (x) 20 #define BASE_NUMERICS_UNLIKELY(x) (x) 21 #endif 22 23 namespace base { 24 namespace internal { 25 26 // The std library doesn't provide a binary max_exponent for integers, however 27 // we can compute an analog using std::numeric_limits<>::digits. 28 template <typename NumericType> 29 struct MaxExponent { 30 static const int value = std::is_floating_point_v<NumericType> 31 ? std::numeric_limits<NumericType>::max_exponent 32 : std::numeric_limits<NumericType>::digits + 1; 33 }; 34 35 // The number of bits (including the sign) in an integer. Eliminates sizeof 36 // hacks. 37 template <typename NumericType> 38 struct IntegerBitsPlusSign { 39 static const int value = 40 std::numeric_limits<NumericType>::digits + std::is_signed_v<NumericType>; 41 }; 42 43 // Helper templates for integer manipulations. 44 45 template <typename Integer> 46 struct PositionOfSignBit { 47 static const size_t value = IntegerBitsPlusSign<Integer>::value - 1; 48 }; 49 50 // Determines if a numeric value is negative without throwing compiler 51 // warnings on: unsigned(value) < 0. 52 template <typename T, std::enable_if_t<std::is_signed_v<T>>* = nullptr> 53 constexpr bool IsValueNegative(T value) { 54 static_assert(std::is_arithmetic_v<T>, "Argument must be numeric."); 55 return value < 0; 56 } 57 58 template <typename T, std::enable_if_t<!std::is_signed_v<T>>* = nullptr> 59 constexpr bool IsValueNegative(T) { 60 static_assert(std::is_arithmetic_v<T>, "Argument must be numeric."); 61 return false; 62 } 63 64 // This performs a fast negation, returning a signed value. It works on unsigned 65 // arguments, but probably doesn't do what you want for any unsigned value 66 // larger than max / 2 + 1 (i.e. signed min cast to unsigned). 67 template <typename T> 68 constexpr typename std::make_signed<T>::type ConditionalNegate( 69 T x, 70 bool is_negative) { 71 static_assert(std::is_integral_v<T>, "Type must be integral"); 72 using SignedT = typename std::make_signed<T>::type; 73 using UnsignedT = typename std::make_unsigned<T>::type; 74 return static_cast<SignedT>((static_cast<UnsignedT>(x) ^ 75 static_cast<UnsignedT>(-SignedT(is_negative))) + 76 is_negative); 77 } 78 79 // This performs a safe, absolute value via unsigned overflow. 80 template <typename T> 81 constexpr typename std::make_unsigned<T>::type SafeUnsignedAbs(T value) { 82 static_assert(std::is_integral_v<T>, "Type must be integral"); 83 using UnsignedT = typename std::make_unsigned<T>::type; 84 return IsValueNegative(value) 85 ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value)) 86 : static_cast<UnsignedT>(value); 87 } 88 89 // TODO(jschuh): Switch to std::is_constant_evaluated() once C++20 is supported. 90 // Alternately, the usage could be restructured for "consteval if" in C++23. 91 #if HAS_BUILTIN(__builtin_is_constant_evaluated) 92 #define IsConstantEvaluated() (__builtin_is_constant_evaluated()) 93 #else 94 #define IsConstantEvaluated() false 95 #endif 96 97 // TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict 98 // some accelerated runtime paths to release builds until this can be forced 99 // with consteval support in C++20 or C++23. 100 #if defined(NDEBUG) 101 constexpr bool kEnableAsmCode = true; 102 #else 103 constexpr bool kEnableAsmCode = false; 104 #endif 105 106 // Forces a crash, like a CHECK(false). Used for numeric boundary errors. 107 // Also used in a constexpr template to trigger a compilation failure on 108 // an error condition. 109 struct CheckOnFailure { 110 template <typename T> 111 static T HandleFailure() { 112 #if defined(_MSC_VER) 113 __debugbreak(); 114 #elif defined(__GNUC__) || defined(__clang__) 115 __builtin_trap(); 116 #else 117 ((void)(*(volatile char*)0 = 0)); 118 #endif 119 return T(); 120 } 121 }; 122 123 enum IntegerRepresentation { 124 INTEGER_REPRESENTATION_UNSIGNED, 125 INTEGER_REPRESENTATION_SIGNED 126 }; 127 128 // A range for a given nunmeric Src type is contained for a given numeric Dst 129 // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and 130 // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. 131 // We implement this as template specializations rather than simple static 132 // comparisons to ensure type correctness in our comparisons. 133 enum NumericRangeRepresentation { 134 NUMERIC_RANGE_NOT_CONTAINED, 135 NUMERIC_RANGE_CONTAINED 136 }; 137 138 // Helper templates to statically determine if our destination type can contain 139 // maximum and minimum values represented by the source type. 140 141 template <typename Dst, 142 typename Src, 143 IntegerRepresentation DstSign = std::is_signed_v<Dst> 144 ? INTEGER_REPRESENTATION_SIGNED 145 : INTEGER_REPRESENTATION_UNSIGNED, 146 IntegerRepresentation SrcSign = std::is_signed_v<Src> 147 ? INTEGER_REPRESENTATION_SIGNED 148 : INTEGER_REPRESENTATION_UNSIGNED> 149 struct StaticDstRangeRelationToSrcRange; 150 151 // Same sign: Dst is guaranteed to contain Src only if its range is equal or 152 // larger. 153 template <typename Dst, typename Src, IntegerRepresentation Sign> 154 struct StaticDstRangeRelationToSrcRange<Dst, Src, Sign, Sign> { 155 static const NumericRangeRepresentation value = 156 MaxExponent<Dst>::value >= MaxExponent<Src>::value 157 ? NUMERIC_RANGE_CONTAINED 158 : NUMERIC_RANGE_NOT_CONTAINED; 159 }; 160 161 // Unsigned to signed: Dst is guaranteed to contain source only if its range is 162 // larger. 163 template <typename Dst, typename Src> 164 struct StaticDstRangeRelationToSrcRange<Dst, 165 Src, 166 INTEGER_REPRESENTATION_SIGNED, 167 INTEGER_REPRESENTATION_UNSIGNED> { 168 static const NumericRangeRepresentation value = 169 MaxExponent<Dst>::value > MaxExponent<Src>::value 170 ? NUMERIC_RANGE_CONTAINED 171 : NUMERIC_RANGE_NOT_CONTAINED; 172 }; 173 174 // Signed to unsigned: Dst cannot be statically determined to contain Src. 175 template <typename Dst, typename Src> 176 struct StaticDstRangeRelationToSrcRange<Dst, 177 Src, 178 INTEGER_REPRESENTATION_UNSIGNED, 179 INTEGER_REPRESENTATION_SIGNED> { 180 static const NumericRangeRepresentation value = NUMERIC_RANGE_NOT_CONTAINED; 181 }; 182 183 // This class wraps the range constraints as separate booleans so the compiler 184 // can identify constants and eliminate unused code paths. 185 class RangeCheck { 186 public: 187 constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) 188 : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} 189 constexpr RangeCheck() : is_underflow_(false), is_overflow_(false) {} 190 constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } 191 constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } 192 constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } 193 constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } 194 constexpr bool IsOverflowFlagSet() const { return is_overflow_; } 195 constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } 196 constexpr bool operator==(const RangeCheck rhs) const { 197 return is_underflow_ == rhs.is_underflow_ && 198 is_overflow_ == rhs.is_overflow_; 199 } 200 constexpr bool operator!=(const RangeCheck rhs) const { 201 return !(*this == rhs); 202 } 203 204 private: 205 // Do not change the order of these member variables. The integral conversion 206 // optimization depends on this exact order. 207 const bool is_underflow_; 208 const bool is_overflow_; 209 }; 210 211 // The following helper template addresses a corner case in range checks for 212 // conversion from a floating-point type to an integral type of smaller range 213 // but larger precision (e.g. float -> unsigned). The problem is as follows: 214 // 1. Integral maximum is always one less than a power of two, so it must be 215 // truncated to fit the mantissa of the floating point. The direction of 216 // rounding is implementation defined, but by default it's always IEEE 217 // floats, which round to nearest and thus result in a value of larger 218 // magnitude than the integral value. 219 // Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX 220 // // is 4294967295u. 221 // 2. If the floating point value is equal to the promoted integral maximum 222 // value, a range check will erroneously pass. 223 // Example: (4294967296f <= 4294967295u) // This is true due to a precision 224 // // loss in rounding up to float. 225 // 3. When the floating point value is then converted to an integral, the 226 // resulting value is out of range for the target integral type and 227 // thus is implementation defined. 228 // Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. 229 // To fix this bug we manually truncate the maximum value when the destination 230 // type is an integral of larger precision than the source floating-point type, 231 // such that the resulting maximum is represented exactly as a floating point. 232 template <typename Dst, typename Src, template <typename> class Bounds> 233 struct NarrowingRange { 234 using SrcLimits = std::numeric_limits<Src>; 235 using DstLimits = typename std::numeric_limits<Dst>; 236 237 // Computes the mask required to make an accurate comparison between types. 238 static const int kShift = 239 (MaxExponent<Src>::value > MaxExponent<Dst>::value && 240 SrcLimits::digits < DstLimits::digits) 241 ? (DstLimits::digits - SrcLimits::digits) 242 : 0; 243 template <typename T, std::enable_if_t<std::is_integral_v<T>>* = nullptr> 244 245 // Masks out the integer bits that are beyond the precision of the 246 // intermediate type used for comparison. 247 static constexpr T Adjust(T value) { 248 static_assert(std::is_same_v<T, Dst>, ""); 249 static_assert(kShift < DstLimits::digits, ""); 250 using UnsignedDst = typename std::make_unsigned_t<T>; 251 return static_cast<T>(ConditionalNegate( 252 SafeUnsignedAbs(value) & ~((UnsignedDst{1} << kShift) - UnsignedDst{1}), 253 IsValueNegative(value))); 254 } 255 256 template <typename T, 257 std::enable_if_t<std::is_floating_point_v<T>>* = nullptr> 258 static constexpr T Adjust(T value) { 259 static_assert(std::is_same_v<T, Dst>, ""); 260 static_assert(kShift == 0, ""); 261 return value; 262 } 263 264 static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } 265 static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } 266 }; 267 268 template <typename Dst, 269 typename Src, 270 template <typename> 271 class Bounds, 272 IntegerRepresentation DstSign = std::is_signed_v<Dst> 273 ? INTEGER_REPRESENTATION_SIGNED 274 : INTEGER_REPRESENTATION_UNSIGNED, 275 IntegerRepresentation SrcSign = std::is_signed_v<Src> 276 ? INTEGER_REPRESENTATION_SIGNED 277 : INTEGER_REPRESENTATION_UNSIGNED, 278 NumericRangeRepresentation DstRange = 279 StaticDstRangeRelationToSrcRange<Dst, Src>::value> 280 struct DstRangeRelationToSrcRangeImpl; 281 282 // The following templates are for ranges that must be verified at runtime. We 283 // split it into checks based on signedness to avoid confusing casts and 284 // compiler warnings on signed an unsigned comparisons. 285 286 // Same sign narrowing: The range is contained for normal limits. 287 template <typename Dst, 288 typename Src, 289 template <typename> 290 class Bounds, 291 IntegerRepresentation DstSign, 292 IntegerRepresentation SrcSign> 293 struct DstRangeRelationToSrcRangeImpl<Dst, 294 Src, 295 Bounds, 296 DstSign, 297 SrcSign, 298 NUMERIC_RANGE_CONTAINED> { 299 static constexpr RangeCheck Check(Src value) { 300 using SrcLimits = std::numeric_limits<Src>; 301 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 302 return RangeCheck( 303 static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || 304 static_cast<Dst>(value) >= DstLimits::lowest(), 305 static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || 306 static_cast<Dst>(value) <= DstLimits::max()); 307 } 308 }; 309 310 // Signed to signed narrowing: Both the upper and lower boundaries may be 311 // exceeded for standard limits. 312 template <typename Dst, typename Src, template <typename> class Bounds> 313 struct DstRangeRelationToSrcRangeImpl<Dst, 314 Src, 315 Bounds, 316 INTEGER_REPRESENTATION_SIGNED, 317 INTEGER_REPRESENTATION_SIGNED, 318 NUMERIC_RANGE_NOT_CONTAINED> { 319 static constexpr RangeCheck Check(Src value) { 320 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 321 return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); 322 } 323 }; 324 325 // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for 326 // standard limits. 327 template <typename Dst, typename Src, template <typename> class Bounds> 328 struct DstRangeRelationToSrcRangeImpl<Dst, 329 Src, 330 Bounds, 331 INTEGER_REPRESENTATION_UNSIGNED, 332 INTEGER_REPRESENTATION_UNSIGNED, 333 NUMERIC_RANGE_NOT_CONTAINED> { 334 static constexpr RangeCheck Check(Src value) { 335 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 336 return RangeCheck( 337 DstLimits::lowest() == Dst(0) || value >= DstLimits::lowest(), 338 value <= DstLimits::max()); 339 } 340 }; 341 342 // Unsigned to signed: Only the upper bound can be exceeded for standard limits. 343 template <typename Dst, typename Src, template <typename> class Bounds> 344 struct DstRangeRelationToSrcRangeImpl<Dst, 345 Src, 346 Bounds, 347 INTEGER_REPRESENTATION_SIGNED, 348 INTEGER_REPRESENTATION_UNSIGNED, 349 NUMERIC_RANGE_NOT_CONTAINED> { 350 static constexpr RangeCheck Check(Src value) { 351 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 352 using Promotion = decltype(Src() + Dst()); 353 return RangeCheck(DstLimits::lowest() <= Dst(0) || 354 static_cast<Promotion>(value) >= 355 static_cast<Promotion>(DstLimits::lowest()), 356 static_cast<Promotion>(value) <= 357 static_cast<Promotion>(DstLimits::max())); 358 } 359 }; 360 361 // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, 362 // and any negative value exceeds the lower boundary for standard limits. 363 template <typename Dst, typename Src, template <typename> class Bounds> 364 struct DstRangeRelationToSrcRangeImpl<Dst, 365 Src, 366 Bounds, 367 INTEGER_REPRESENTATION_UNSIGNED, 368 INTEGER_REPRESENTATION_SIGNED, 369 NUMERIC_RANGE_NOT_CONTAINED> { 370 static constexpr RangeCheck Check(Src value) { 371 using SrcLimits = std::numeric_limits<Src>; 372 using DstLimits = NarrowingRange<Dst, Src, Bounds>; 373 using Promotion = decltype(Src() + Dst()); 374 bool ge_zero = false; 375 // Converting floating-point to integer will discard fractional part, so 376 // values in (-1.0, -0.0) will truncate to 0 and fit in Dst. 377 if (std::is_floating_point_v<Src>) { 378 ge_zero = value > Src(-1); 379 } else { 380 ge_zero = value >= Src(0); 381 } 382 return RangeCheck( 383 ge_zero && (DstLimits::lowest() == 0 || 384 static_cast<Dst>(value) >= DstLimits::lowest()), 385 static_cast<Promotion>(SrcLimits::max()) <= 386 static_cast<Promotion>(DstLimits::max()) || 387 static_cast<Promotion>(value) <= 388 static_cast<Promotion>(DstLimits::max())); 389 } 390 }; 391 392 // Simple wrapper for statically checking if a type's range is contained. 393 template <typename Dst, typename Src> 394 struct IsTypeInRangeForNumericType { 395 static const bool value = StaticDstRangeRelationToSrcRange<Dst, Src>::value == 396 NUMERIC_RANGE_CONTAINED; 397 }; 398 399 template <typename Dst, 400 template <typename> class Bounds = std::numeric_limits, 401 typename Src> 402 constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { 403 static_assert(std::is_arithmetic_v<Src>, "Argument must be numeric."); 404 static_assert(std::is_arithmetic_v<Dst>, "Result must be numeric."); 405 static_assert(Bounds<Dst>::lowest() < Bounds<Dst>::max(), ""); 406 return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); 407 } 408 409 // Integer promotion templates used by the portable checked integer arithmetic. 410 template <size_t Size, bool IsSigned> 411 struct IntegerForDigitsAndSign; 412 413 #define INTEGER_FOR_DIGITS_AND_SIGN(I) \ 414 template <> \ 415 struct IntegerForDigitsAndSign<IntegerBitsPlusSign<I>::value, \ 416 std::is_signed_v<I>> { \ 417 using type = I; \ 418 } 419 420 INTEGER_FOR_DIGITS_AND_SIGN(int8_t); 421 INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); 422 INTEGER_FOR_DIGITS_AND_SIGN(int16_t); 423 INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); 424 INTEGER_FOR_DIGITS_AND_SIGN(int32_t); 425 INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); 426 INTEGER_FOR_DIGITS_AND_SIGN(int64_t); 427 INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); 428 #undef INTEGER_FOR_DIGITS_AND_SIGN 429 430 // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to 431 // support 128-bit math, then the ArithmeticPromotion template below will need 432 // to be updated (or more likely replaced with a decltype expression). 433 static_assert(IntegerBitsPlusSign<intmax_t>::value == 64, 434 "Max integer size not supported for this toolchain."); 435 436 template <typename Integer, bool IsSigned = std::is_signed_v<Integer>> 437 struct TwiceWiderInteger { 438 using type = 439 typename IntegerForDigitsAndSign<IntegerBitsPlusSign<Integer>::value * 2, 440 IsSigned>::type; 441 }; 442 443 enum ArithmeticPromotionCategory { 444 LEFT_PROMOTION, // Use the type of the left-hand argument. 445 RIGHT_PROMOTION // Use the type of the right-hand argument. 446 }; 447 448 // Determines the type that can represent the largest positive value. 449 template <typename Lhs, 450 typename Rhs, 451 ArithmeticPromotionCategory Promotion = 452 (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value) 453 ? LEFT_PROMOTION 454 : RIGHT_PROMOTION> 455 struct MaxExponentPromotion; 456 457 template <typename Lhs, typename Rhs> 458 struct MaxExponentPromotion<Lhs, Rhs, LEFT_PROMOTION> { 459 using type = Lhs; 460 }; 461 462 template <typename Lhs, typename Rhs> 463 struct MaxExponentPromotion<Lhs, Rhs, RIGHT_PROMOTION> { 464 using type = Rhs; 465 }; 466 467 // Determines the type that can represent the lowest arithmetic value. 468 template <typename Lhs, 469 typename Rhs, 470 ArithmeticPromotionCategory Promotion = 471 std::is_signed_v<Lhs> 472 ? (std::is_signed_v<Rhs> 473 ? (MaxExponent<Lhs>::value > MaxExponent<Rhs>::value 474 ? LEFT_PROMOTION 475 : RIGHT_PROMOTION) 476 : LEFT_PROMOTION) 477 : (std::is_signed_v<Rhs> 478 ? RIGHT_PROMOTION 479 : (MaxExponent<Lhs>::value < MaxExponent<Rhs>::value 480 ? LEFT_PROMOTION 481 : RIGHT_PROMOTION))> 482 struct LowestValuePromotion; 483 484 template <typename Lhs, typename Rhs> 485 struct LowestValuePromotion<Lhs, Rhs, LEFT_PROMOTION> { 486 using type = Lhs; 487 }; 488 489 template <typename Lhs, typename Rhs> 490 struct LowestValuePromotion<Lhs, Rhs, RIGHT_PROMOTION> { 491 using type = Rhs; 492 }; 493 494 // Determines the type that is best able to represent an arithmetic result. 495 template < 496 typename Lhs, 497 typename Rhs = Lhs, 498 bool is_intmax_type = 499 std::is_integral_v<typename MaxExponentPromotion<Lhs, Rhs>::type> && 500 IntegerBitsPlusSign<typename MaxExponentPromotion<Lhs, Rhs>::type>:: 501 value == IntegerBitsPlusSign<intmax_t>::value, 502 bool is_max_exponent = StaticDstRangeRelationToSrcRange< 503 typename MaxExponentPromotion<Lhs, Rhs>::type, 504 Lhs>::value == NUMERIC_RANGE_CONTAINED && 505 StaticDstRangeRelationToSrcRange< 506 typename MaxExponentPromotion<Lhs, Rhs>::type, 507 Rhs>::value == NUMERIC_RANGE_CONTAINED> 508 struct BigEnoughPromotion; 509 510 // The side with the max exponent is big enough. 511 template <typename Lhs, typename Rhs, bool is_intmax_type> 512 struct BigEnoughPromotion<Lhs, Rhs, is_intmax_type, true> { 513 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 514 static const bool is_contained = true; 515 }; 516 517 // We can use a twice wider type to fit. 518 template <typename Lhs, typename Rhs> 519 struct BigEnoughPromotion<Lhs, Rhs, false, false> { 520 using type = 521 typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, 522 std::is_signed_v<Lhs> || 523 std::is_signed_v<Rhs>>::type; 524 static const bool is_contained = true; 525 }; 526 527 // No type is large enough. 528 template <typename Lhs, typename Rhs> 529 struct BigEnoughPromotion<Lhs, Rhs, true, false> { 530 using type = typename MaxExponentPromotion<Lhs, Rhs>::type; 531 static const bool is_contained = false; 532 }; 533 534 // We can statically check if operations on the provided types can wrap, so we 535 // can skip the checked operations if they're not needed. So, for an integer we 536 // care if the destination type preserves the sign and is twice the width of 537 // the source. 538 template <typename T, typename Lhs, typename Rhs = Lhs> 539 struct IsIntegerArithmeticSafe { 540 static const bool value = 541 !std::is_floating_point_v<T> && !std::is_floating_point_v<Lhs> && 542 !std::is_floating_point_v<Rhs> && 543 std::is_signed_v<T> >= std::is_signed_v<Lhs> && 544 IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Lhs>::value) && 545 std::is_signed_v<T> >= std::is_signed_v<Rhs> && 546 IntegerBitsPlusSign<T>::value >= (2 * IntegerBitsPlusSign<Rhs>::value); 547 }; 548 549 // Promotes to a type that can represent any possible result of a binary 550 // arithmetic operation with the source types. 551 template <typename Lhs, 552 typename Rhs, 553 bool is_promotion_possible = IsIntegerArithmeticSafe< 554 typename std::conditional<std::is_signed_v<Lhs> || 555 std::is_signed_v<Rhs>, 556 intmax_t, 557 uintmax_t>::type, 558 typename MaxExponentPromotion<Lhs, Rhs>::type>::value> 559 struct FastIntegerArithmeticPromotion; 560 561 template <typename Lhs, typename Rhs> 562 struct FastIntegerArithmeticPromotion<Lhs, Rhs, true> { 563 using type = 564 typename TwiceWiderInteger<typename MaxExponentPromotion<Lhs, Rhs>::type, 565 std::is_signed_v<Lhs> || 566 std::is_signed_v<Rhs>>::type; 567 static_assert(IsIntegerArithmeticSafe<type, Lhs, Rhs>::value, ""); 568 static const bool is_contained = true; 569 }; 570 571 template <typename Lhs, typename Rhs> 572 struct FastIntegerArithmeticPromotion<Lhs, Rhs, false> { 573 using type = typename BigEnoughPromotion<Lhs, Rhs>::type; 574 static const bool is_contained = false; 575 }; 576 577 // Extracts the underlying type from an enum. 578 template <typename T, bool is_enum = std::is_enum_v<T>> 579 struct ArithmeticOrUnderlyingEnum; 580 581 template <typename T> 582 struct ArithmeticOrUnderlyingEnum<T, true> { 583 using type = typename std::underlying_type<T>::type; 584 static const bool value = std::is_arithmetic_v<type>; 585 }; 586 587 template <typename T> 588 struct ArithmeticOrUnderlyingEnum<T, false> { 589 using type = T; 590 static const bool value = std::is_arithmetic_v<type>; 591 }; 592 593 // The following are helper templates used in the CheckedNumeric class. 594 template <typename T> 595 class CheckedNumeric; 596 597 template <typename T> 598 class ClampedNumeric; 599 600 template <typename T> 601 class StrictNumeric; 602 603 // Used to treat CheckedNumeric and arithmetic underlying types the same. 604 template <typename T> 605 struct UnderlyingType { 606 using type = typename ArithmeticOrUnderlyingEnum<T>::type; 607 static const bool is_numeric = std::is_arithmetic_v<type>; 608 static const bool is_checked = false; 609 static const bool is_clamped = false; 610 static const bool is_strict = false; 611 }; 612 613 template <typename T> 614 struct UnderlyingType<CheckedNumeric<T>> { 615 using type = T; 616 static const bool is_numeric = true; 617 static const bool is_checked = true; 618 static const bool is_clamped = false; 619 static const bool is_strict = false; 620 }; 621 622 template <typename T> 623 struct UnderlyingType<ClampedNumeric<T>> { 624 using type = T; 625 static const bool is_numeric = true; 626 static const bool is_checked = false; 627 static const bool is_clamped = true; 628 static const bool is_strict = false; 629 }; 630 631 template <typename T> 632 struct UnderlyingType<StrictNumeric<T>> { 633 using type = T; 634 static const bool is_numeric = true; 635 static const bool is_checked = false; 636 static const bool is_clamped = false; 637 static const bool is_strict = true; 638 }; 639 640 template <typename L, typename R> 641 struct IsCheckedOp { 642 static const bool value = 643 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 644 (UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); 645 }; 646 647 template <typename L, typename R> 648 struct IsClampedOp { 649 static const bool value = 650 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 651 (UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped) && 652 !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked); 653 }; 654 655 template <typename L, typename R> 656 struct IsStrictOp { 657 static const bool value = 658 UnderlyingType<L>::is_numeric && UnderlyingType<R>::is_numeric && 659 (UnderlyingType<L>::is_strict || UnderlyingType<R>::is_strict) && 660 !(UnderlyingType<L>::is_checked || UnderlyingType<R>::is_checked) && 661 !(UnderlyingType<L>::is_clamped || UnderlyingType<R>::is_clamped); 662 }; 663 664 // as_signed<> returns the supplied integral value (or integral castable 665 // Numeric template) cast as a signed integral of equivalent precision. 666 // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) 667 template <typename Src> 668 constexpr typename std::make_signed< 669 typename base::internal::UnderlyingType<Src>::type>::type 670 as_signed(const Src value) { 671 static_assert(std::is_integral_v<decltype(as_signed(value))>, 672 "Argument must be a signed or unsigned integer type."); 673 return static_cast<decltype(as_signed(value))>(value); 674 } 675 676 // as_unsigned<> returns the supplied integral value (or integral castable 677 // Numeric template) cast as an unsigned integral of equivalent precision. 678 // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) 679 template <typename Src> 680 constexpr typename std::make_unsigned< 681 typename base::internal::UnderlyingType<Src>::type>::type 682 as_unsigned(const Src value) { 683 static_assert(std::is_integral_v<decltype(as_unsigned(value))>, 684 "Argument must be a signed or unsigned integer type."); 685 return static_cast<decltype(as_unsigned(value))>(value); 686 } 687 688 template <typename L, typename R> 689 constexpr bool IsLessImpl(const L lhs, 690 const R rhs, 691 const RangeCheck l_range, 692 const RangeCheck r_range) { 693 return l_range.IsUnderflow() || r_range.IsOverflow() || 694 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) < 695 static_cast<decltype(lhs + rhs)>(rhs)); 696 } 697 698 template <typename L, typename R> 699 struct IsLess { 700 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 701 "Types must be numeric."); 702 static constexpr bool Test(const L lhs, const R rhs) { 703 return IsLessImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 704 DstRangeRelationToSrcRange<L>(rhs)); 705 } 706 }; 707 708 template <typename L, typename R> 709 constexpr bool IsLessOrEqualImpl(const L lhs, 710 const R rhs, 711 const RangeCheck l_range, 712 const RangeCheck r_range) { 713 return l_range.IsUnderflow() || r_range.IsOverflow() || 714 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) <= 715 static_cast<decltype(lhs + rhs)>(rhs)); 716 } 717 718 template <typename L, typename R> 719 struct IsLessOrEqual { 720 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 721 "Types must be numeric."); 722 static constexpr bool Test(const L lhs, const R rhs) { 723 return IsLessOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 724 DstRangeRelationToSrcRange<L>(rhs)); 725 } 726 }; 727 728 template <typename L, typename R> 729 constexpr bool IsGreaterImpl(const L lhs, 730 const R rhs, 731 const RangeCheck l_range, 732 const RangeCheck r_range) { 733 return l_range.IsOverflow() || r_range.IsUnderflow() || 734 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) > 735 static_cast<decltype(lhs + rhs)>(rhs)); 736 } 737 738 template <typename L, typename R> 739 struct IsGreater { 740 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 741 "Types must be numeric."); 742 static constexpr bool Test(const L lhs, const R rhs) { 743 return IsGreaterImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 744 DstRangeRelationToSrcRange<L>(rhs)); 745 } 746 }; 747 748 template <typename L, typename R> 749 constexpr bool IsGreaterOrEqualImpl(const L lhs, 750 const R rhs, 751 const RangeCheck l_range, 752 const RangeCheck r_range) { 753 return l_range.IsOverflow() || r_range.IsUnderflow() || 754 (l_range == r_range && static_cast<decltype(lhs + rhs)>(lhs) >= 755 static_cast<decltype(lhs + rhs)>(rhs)); 756 } 757 758 template <typename L, typename R> 759 struct IsGreaterOrEqual { 760 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 761 "Types must be numeric."); 762 static constexpr bool Test(const L lhs, const R rhs) { 763 return IsGreaterOrEqualImpl(lhs, rhs, DstRangeRelationToSrcRange<R>(lhs), 764 DstRangeRelationToSrcRange<L>(rhs)); 765 } 766 }; 767 768 template <typename L, typename R> 769 struct IsEqual { 770 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 771 "Types must be numeric."); 772 static constexpr bool Test(const L lhs, const R rhs) { 773 return DstRangeRelationToSrcRange<R>(lhs) == 774 DstRangeRelationToSrcRange<L>(rhs) && 775 static_cast<decltype(lhs + rhs)>(lhs) == 776 static_cast<decltype(lhs + rhs)>(rhs); 777 } 778 }; 779 780 template <typename L, typename R> 781 struct IsNotEqual { 782 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 783 "Types must be numeric."); 784 static constexpr bool Test(const L lhs, const R rhs) { 785 return DstRangeRelationToSrcRange<R>(lhs) != 786 DstRangeRelationToSrcRange<L>(rhs) || 787 static_cast<decltype(lhs + rhs)>(lhs) != 788 static_cast<decltype(lhs + rhs)>(rhs); 789 } 790 }; 791 792 // These perform the actual math operations on the CheckedNumerics. 793 // Binary arithmetic operations. 794 template <template <typename, typename> class C, typename L, typename R> 795 constexpr bool SafeCompare(const L lhs, const R rhs) { 796 static_assert(std::is_arithmetic_v<L> && std::is_arithmetic_v<R>, 797 "Types must be numeric."); 798 using Promotion = BigEnoughPromotion<L, R>; 799 using BigType = typename Promotion::type; 800 return Promotion::is_contained 801 // Force to a larger type for speed if both are contained. 802 ? C<BigType, BigType>::Test( 803 static_cast<BigType>(static_cast<L>(lhs)), 804 static_cast<BigType>(static_cast<R>(rhs))) 805 // Let the template functions figure it out for mixed types. 806 : C<L, R>::Test(lhs, rhs); 807 } 808 809 template <typename Dst, typename Src> 810 constexpr bool IsMaxInRangeForNumericType() { 811 return IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(), 812 std::numeric_limits<Src>::max()); 813 } 814 815 template <typename Dst, typename Src> 816 constexpr bool IsMinInRangeForNumericType() { 817 return IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(), 818 std::numeric_limits<Src>::lowest()); 819 } 820 821 template <typename Dst, typename Src> 822 constexpr Dst CommonMax() { 823 return !IsMaxInRangeForNumericType<Dst, Src>() 824 ? Dst(std::numeric_limits<Dst>::max()) 825 : Dst(std::numeric_limits<Src>::max()); 826 } 827 828 template <typename Dst, typename Src> 829 constexpr Dst CommonMin() { 830 return !IsMinInRangeForNumericType<Dst, Src>() 831 ? Dst(std::numeric_limits<Dst>::lowest()) 832 : Dst(std::numeric_limits<Src>::lowest()); 833 } 834 835 // This is a wrapper to generate return the max or min for a supplied type. 836 // If the argument is false, the returned value is the maximum. If true the 837 // returned value is the minimum. 838 template <typename Dst, typename Src = Dst> 839 constexpr Dst CommonMaxOrMin(bool is_min) { 840 return is_min ? CommonMin<Dst, Src>() : CommonMax<Dst, Src>(); 841 } 842 843 } // namespace internal 844 } // namespace base 845 846 #endif // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_