|  | // Copyright 2014 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #ifndef BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 
|  | #define BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ | 
|  |  | 
|  | // IWYU pragma: private, include "base/numerics/safe_conversions.h" | 
|  |  | 
|  | #include <stddef.h> | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <concepts> | 
|  | #include <limits> | 
|  | #include <type_traits> | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/numerics/integral_constant_like.h" | 
|  |  | 
|  | namespace base::internal { | 
|  |  | 
|  | // The std library doesn't provide a binary max_exponent for integers, however | 
|  | // we can compute an analog using std::numeric_limits<>::digits. | 
|  | template <typename NumericType> | 
|  | inline constexpr int kMaxExponent = | 
|  | std::is_floating_point_v<NumericType> | 
|  | ? std::numeric_limits<NumericType>::max_exponent | 
|  | : std::numeric_limits<NumericType>::digits + 1; | 
|  |  | 
|  | // The number of bits (including the sign) in an integer. Eliminates sizeof | 
|  | // hacks. | 
|  | template <typename NumericType> | 
|  | inline constexpr int kIntegerBitsPlusSign = | 
|  | std::numeric_limits<NumericType>::digits + std::is_signed_v<NumericType>; | 
|  |  | 
|  | // Determines if a numeric value is negative without throwing compiler | 
|  | // warnings on: unsigned(value) < 0. | 
|  | template <typename T> | 
|  | requires(std::is_arithmetic_v<T>) | 
|  | constexpr bool IsValueNegative(T value) { | 
|  | if constexpr (std::is_signed_v<T>) { | 
|  | return value < 0; | 
|  | } else { | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // This performs a fast negation, returning a signed value. It works on unsigned | 
|  | // arguments, but probably doesn't do what you want for any unsigned value | 
|  | // larger than max / 2 + 1 (i.e. signed min cast to unsigned). | 
|  | template <typename T> | 
|  | requires std::is_integral_v<T> | 
|  | constexpr auto ConditionalNegate(T x, bool is_negative) { | 
|  | using SignedT = std::make_signed_t<T>; | 
|  | using UnsignedT = std::make_unsigned_t<T>; | 
|  | return static_cast<SignedT>((static_cast<UnsignedT>(x) ^ | 
|  | static_cast<UnsignedT>(-SignedT(is_negative))) + | 
|  | is_negative); | 
|  | } | 
|  |  | 
|  | // This performs a safe, absolute value via unsigned overflow. | 
|  | template <typename T> | 
|  | requires std::is_integral_v<T> | 
|  | constexpr auto SafeUnsignedAbs(T value) { | 
|  | using UnsignedT = std::make_unsigned_t<T>; | 
|  | return IsValueNegative(value) | 
|  | ? static_cast<UnsignedT>(0u - static_cast<UnsignedT>(value)) | 
|  | : static_cast<UnsignedT>(value); | 
|  | } | 
|  |  | 
|  | // TODO(jschuh): Debug builds don't reliably propagate constants, so we restrict | 
|  | // some accelerated runtime paths to release builds until this can be forced | 
|  | // with consteval support in C++20 or C++23. | 
|  | #if defined(NDEBUG) | 
|  | inline constexpr bool kEnableAsmCode = true; | 
|  | #else | 
|  | inline constexpr bool kEnableAsmCode = false; | 
|  | #endif | 
|  |  | 
|  | // Forces a crash, like a NOTREACHED(). Used for numeric boundary errors. | 
|  | // Also used in a constexpr template to trigger a compilation failure on | 
|  | // an error condition. | 
|  | struct CheckOnFailure { | 
|  | template <typename T> | 
|  | static T HandleFailure() { | 
|  | #if defined(_MSC_VER) | 
|  | __debugbreak(); | 
|  | #elif defined(__GNUC__) || defined(__clang__) | 
|  | __builtin_trap(); | 
|  | #else | 
|  | ((void)(*(volatile char*)0 = 0)); | 
|  | #endif | 
|  | return T(); | 
|  | } | 
|  | }; | 
|  |  | 
|  | enum class IntegerRepresentation { kUnsigned, kSigned }; | 
|  |  | 
|  | // A range for a given nunmeric Src type is contained for a given numeric Dst | 
|  | // type if both numeric_limits<Src>::max() <= numeric_limits<Dst>::max() and | 
|  | // numeric_limits<Src>::lowest() >= numeric_limits<Dst>::lowest() are true. | 
|  | // We implement this as template specializations rather than simple static | 
|  | // comparisons to ensure type correctness in our comparisons. | 
|  | enum class NumericRangeRepresentation { kNotContained, kContained }; | 
|  |  | 
|  | // Helper templates to statically determine if our destination type can contain | 
|  | // maximum and minimum values represented by the source type. | 
|  |  | 
|  | // Default case, used for same sign: Dst is guaranteed to contain Src only if | 
|  | // its range is equal or larger. | 
|  | template <typename Dst, | 
|  | typename Src, | 
|  | IntegerRepresentation DstSign = | 
|  | std::is_signed_v<Dst> ? IntegerRepresentation::kSigned | 
|  | : IntegerRepresentation::kUnsigned, | 
|  | IntegerRepresentation SrcSign = | 
|  | std::is_signed_v<Src> ? IntegerRepresentation::kSigned | 
|  | : IntegerRepresentation::kUnsigned> | 
|  | inline constexpr auto kStaticDstRangeRelationToSrcRange = | 
|  | kMaxExponent<Dst> >= kMaxExponent<Src> | 
|  | ? NumericRangeRepresentation::kContained | 
|  | : NumericRangeRepresentation::kNotContained; | 
|  |  | 
|  | // Unsigned to signed: Dst is guaranteed to contain source only if its range is | 
|  | // larger. | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr auto | 
|  | kStaticDstRangeRelationToSrcRange<Dst, | 
|  | Src, | 
|  | IntegerRepresentation::kSigned, | 
|  | IntegerRepresentation::kUnsigned> = | 
|  | kMaxExponent<Dst> > kMaxExponent<Src> | 
|  | ? NumericRangeRepresentation::kContained | 
|  | : NumericRangeRepresentation::kNotContained; | 
|  |  | 
|  | // Signed to unsigned: Dst cannot be statically determined to contain Src. | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr auto | 
|  | kStaticDstRangeRelationToSrcRange<Dst, | 
|  | Src, | 
|  | IntegerRepresentation::kUnsigned, | 
|  | IntegerRepresentation::kSigned> = | 
|  | NumericRangeRepresentation::kNotContained; | 
|  |  | 
|  | // This class wraps the range constraints as separate booleans so the compiler | 
|  | // can identify constants and eliminate unused code paths. | 
|  | class RangeCheck { | 
|  | public: | 
|  | constexpr RangeCheck() = default; | 
|  | constexpr RangeCheck(bool is_in_lower_bound, bool is_in_upper_bound) | 
|  | : is_underflow_(!is_in_lower_bound), is_overflow_(!is_in_upper_bound) {} | 
|  |  | 
|  | constexpr bool operator==(const RangeCheck& rhs) const = default; | 
|  |  | 
|  | constexpr bool IsValid() const { return !is_overflow_ && !is_underflow_; } | 
|  | constexpr bool IsInvalid() const { return is_overflow_ && is_underflow_; } | 
|  | constexpr bool IsOverflow() const { return is_overflow_ && !is_underflow_; } | 
|  | constexpr bool IsUnderflow() const { return !is_overflow_ && is_underflow_; } | 
|  | constexpr bool IsOverflowFlagSet() const { return is_overflow_; } | 
|  | constexpr bool IsUnderflowFlagSet() const { return is_underflow_; } | 
|  |  | 
|  | private: | 
|  | // Do not change the order of these member variables. The integral conversion | 
|  | // optimization depends on this exact order. | 
|  | const bool is_underflow_ = false; | 
|  | const bool is_overflow_ = false; | 
|  | }; | 
|  |  | 
|  | // The following helper template addresses a corner case in range checks for | 
|  | // conversion from a floating-point type to an integral type of smaller range | 
|  | // but larger precision (e.g. float -> unsigned). The problem is as follows: | 
|  | //   1. Integral maximum is always one less than a power of two, so it must be | 
|  | //      truncated to fit the mantissa of the floating point. The direction of | 
|  | //      rounding is implementation defined, but by default it's always IEEE | 
|  | //      floats, which round to nearest and thus result in a value of larger | 
|  | //      magnitude than the integral value. | 
|  | //      Example: float f = UINT_MAX; // f is 4294967296f but UINT_MAX | 
|  | //                                   // is 4294967295u. | 
|  | //   2. If the floating point value is equal to the promoted integral maximum | 
|  | //      value, a range check will erroneously pass. | 
|  | //      Example: (4294967296f <= 4294967295u) // This is true due to a precision | 
|  | //                                            // loss in rounding up to float. | 
|  | //   3. When the floating point value is then converted to an integral, the | 
|  | //      resulting value is out of range for the target integral type and | 
|  | //      thus is implementation defined. | 
|  | //      Example: unsigned u = (float)INT_MAX; // u will typically overflow to 0. | 
|  | // To fix this bug we manually truncate the maximum value when the destination | 
|  | // type is an integral of larger precision than the source floating-point type, | 
|  | // such that the resulting maximum is represented exactly as a floating point. | 
|  | template <typename Dst, typename Src, template <typename> class Bounds> | 
|  | struct NarrowingRange { | 
|  | using SrcLimits = std::numeric_limits<Src>; | 
|  | using DstLimits = std::numeric_limits<Dst>; | 
|  |  | 
|  | // Computes the mask required to make an accurate comparison between types. | 
|  | static constexpr int kShift = (kMaxExponent<Src> > kMaxExponent<Dst> && | 
|  | SrcLimits::digits < DstLimits::digits) | 
|  | ? (DstLimits::digits - SrcLimits::digits) | 
|  | : 0; | 
|  |  | 
|  | template <typename T> | 
|  | requires(std::same_as<T, Dst> && | 
|  | ((std::integral<T> && kShift < DstLimits::digits) || | 
|  | (std::floating_point<T> && kShift == 0))) | 
|  | // Masks out the integer bits that are beyond the precision of the | 
|  | // intermediate type used for comparison. | 
|  | static constexpr T Adjust(T value) { | 
|  | if constexpr (std::integral<T>) { | 
|  | using UnsignedDst = typename std::make_unsigned_t<T>; | 
|  | return static_cast<T>( | 
|  | ConditionalNegate(SafeUnsignedAbs(value) & | 
|  | ~((UnsignedDst{1} << kShift) - UnsignedDst{1}), | 
|  | IsValueNegative(value))); | 
|  | } else { | 
|  | return value; | 
|  | } | 
|  | } | 
|  |  | 
|  | static constexpr Dst max() { return Adjust(Bounds<Dst>::max()); } | 
|  | static constexpr Dst lowest() { return Adjust(Bounds<Dst>::lowest()); } | 
|  | }; | 
|  |  | 
|  | // The following templates are for ranges that must be verified at runtime. We | 
|  | // split it into checks based on signedness to avoid confusing casts and | 
|  | // compiler warnings on signed an unsigned comparisons. | 
|  |  | 
|  | // Default case, used for same sign narrowing: The range is contained for normal | 
|  | // limits. | 
|  | template <typename Dst, | 
|  | typename Src, | 
|  | template <typename> | 
|  | class Bounds, | 
|  | IntegerRepresentation DstSign = | 
|  | std::is_signed_v<Dst> ? IntegerRepresentation::kSigned | 
|  | : IntegerRepresentation::kUnsigned, | 
|  | IntegerRepresentation SrcSign = | 
|  | std::is_signed_v<Src> ? IntegerRepresentation::kSigned | 
|  | : IntegerRepresentation::kUnsigned, | 
|  | NumericRangeRepresentation DstRange = | 
|  | kStaticDstRangeRelationToSrcRange<Dst, Src>> | 
|  | struct DstRangeRelationToSrcRangeImpl { | 
|  | static constexpr RangeCheck Check(Src value) { | 
|  | using SrcLimits = std::numeric_limits<Src>; | 
|  | using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 
|  | return RangeCheck( | 
|  | static_cast<Dst>(SrcLimits::lowest()) >= DstLimits::lowest() || | 
|  | static_cast<Dst>(value) >= DstLimits::lowest(), | 
|  | static_cast<Dst>(SrcLimits::max()) <= DstLimits::max() || | 
|  | static_cast<Dst>(value) <= DstLimits::max()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Signed to signed narrowing: Both the upper and lower boundaries may be | 
|  | // exceeded for standard limits. | 
|  | template <typename Dst, typename Src, template <typename> class Bounds> | 
|  | struct DstRangeRelationToSrcRangeImpl< | 
|  | Dst, | 
|  | Src, | 
|  | Bounds, | 
|  | IntegerRepresentation::kSigned, | 
|  | IntegerRepresentation::kSigned, | 
|  | NumericRangeRepresentation::kNotContained> { | 
|  | static constexpr RangeCheck Check(Src value) { | 
|  | using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 
|  | return RangeCheck(value >= DstLimits::lowest(), value <= DstLimits::max()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Unsigned to unsigned narrowing: Only the upper bound can be exceeded for | 
|  | // standard limits. | 
|  | template <typename Dst, typename Src, template <typename> class Bounds> | 
|  | struct DstRangeRelationToSrcRangeImpl< | 
|  | Dst, | 
|  | Src, | 
|  | Bounds, | 
|  | IntegerRepresentation::kUnsigned, | 
|  | IntegerRepresentation::kUnsigned, | 
|  | NumericRangeRepresentation::kNotContained> { | 
|  | static constexpr RangeCheck Check(Src value) { | 
|  | using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 
|  | return RangeCheck( | 
|  | DstLimits::lowest() == Dst{0} || value >= DstLimits::lowest(), | 
|  | value <= DstLimits::max()); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Unsigned to signed: Only the upper bound can be exceeded for standard limits. | 
|  | template <typename Dst, typename Src, template <typename> class Bounds> | 
|  | struct DstRangeRelationToSrcRangeImpl< | 
|  | Dst, | 
|  | Src, | 
|  | Bounds, | 
|  | IntegerRepresentation::kSigned, | 
|  | IntegerRepresentation::kUnsigned, | 
|  | NumericRangeRepresentation::kNotContained> { | 
|  | static constexpr RangeCheck Check(Src value) { | 
|  | using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 
|  | using Promotion = decltype(Src() + Dst()); | 
|  | return RangeCheck(DstLimits::lowest() <= Dst{0} || | 
|  | static_cast<Promotion>(value) >= | 
|  | static_cast<Promotion>(DstLimits::lowest()), | 
|  | static_cast<Promotion>(value) <= | 
|  | static_cast<Promotion>(DstLimits::max())); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Signed to unsigned: The upper boundary may be exceeded for a narrower Dst, | 
|  | // and any negative value exceeds the lower boundary for standard limits. | 
|  | template <typename Dst, typename Src, template <typename> class Bounds> | 
|  | struct DstRangeRelationToSrcRangeImpl< | 
|  | Dst, | 
|  | Src, | 
|  | Bounds, | 
|  | IntegerRepresentation::kUnsigned, | 
|  | IntegerRepresentation::kSigned, | 
|  | NumericRangeRepresentation::kNotContained> { | 
|  | static constexpr RangeCheck Check(Src value) { | 
|  | using SrcLimits = std::numeric_limits<Src>; | 
|  | using DstLimits = NarrowingRange<Dst, Src, Bounds>; | 
|  | using Promotion = decltype(Src() + Dst()); | 
|  | bool ge_zero; | 
|  | // Converting floating-point to integer will discard fractional part, so | 
|  | // values in (-1.0, -0.0) will truncate to 0 and fit in Dst. | 
|  | if constexpr (std::is_floating_point_v<Src>) { | 
|  | ge_zero = value > Src{-1}; | 
|  | } else { | 
|  | ge_zero = value >= Src{0}; | 
|  | } | 
|  | return RangeCheck( | 
|  | ge_zero && (DstLimits::lowest() == 0 || | 
|  | static_cast<Dst>(value) >= DstLimits::lowest()), | 
|  | static_cast<Promotion>(SrcLimits::max()) <= | 
|  | static_cast<Promotion>(DstLimits::max()) || | 
|  | static_cast<Promotion>(value) <= | 
|  | static_cast<Promotion>(DstLimits::max())); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Simple wrapper for statically checking if a type's range is contained. | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr bool kIsTypeInRangeForNumericType = | 
|  | kStaticDstRangeRelationToSrcRange<Dst, Src> == | 
|  | NumericRangeRepresentation::kContained; | 
|  |  | 
|  | template <typename Dst, | 
|  | template <typename> class Bounds = std::numeric_limits, | 
|  | typename Src> | 
|  | requires(std::is_arithmetic_v<Src> && std::is_arithmetic_v<Dst> && | 
|  | Bounds<Dst>::lowest() < Bounds<Dst>::max()) | 
|  | constexpr RangeCheck DstRangeRelationToSrcRange(Src value) { | 
|  | return DstRangeRelationToSrcRangeImpl<Dst, Src, Bounds>::Check(value); | 
|  | } | 
|  |  | 
|  | // Integer promotion templates used by the portable checked integer arithmetic. | 
|  | template <size_t Size, bool IsSigned> | 
|  | struct IntegerForDigitsAndSignImpl; | 
|  |  | 
|  | #define INTEGER_FOR_DIGITS_AND_SIGN(I)                        \ | 
|  | template <>                                                 \ | 
|  | struct IntegerForDigitsAndSignImpl<kIntegerBitsPlusSign<I>, \ | 
|  | std::is_signed_v<I>> {   \ | 
|  | using type = I;                                           \ | 
|  | } | 
|  |  | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(int8_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(uint8_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(int16_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(uint16_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(int32_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(uint32_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(int64_t); | 
|  | INTEGER_FOR_DIGITS_AND_SIGN(uint64_t); | 
|  | #undef INTEGER_FOR_DIGITS_AND_SIGN | 
|  |  | 
|  | template <size_t Size, bool IsSigned> | 
|  | using IntegerForDigitsAndSign = | 
|  | IntegerForDigitsAndSignImpl<Size, IsSigned>::type; | 
|  |  | 
|  | // WARNING: We have no IntegerForSizeAndSign<16, *>. If we ever add one to | 
|  | // support 128-bit math, then the ArithmeticPromotion template below will need | 
|  | // to be updated (or more likely replaced with a decltype expression). | 
|  | static_assert(kIntegerBitsPlusSign<intmax_t> == 64, | 
|  | "Max integer size not supported for this toolchain."); | 
|  |  | 
|  | template <typename Integer, bool IsSigned = std::is_signed_v<Integer>> | 
|  | using TwiceWiderInteger = | 
|  | IntegerForDigitsAndSign<kIntegerBitsPlusSign<Integer> * 2, IsSigned>; | 
|  |  | 
|  | // Determines the type that can represent the largest positive value. | 
|  | template <typename Lhs, typename Rhs> | 
|  | using MaxExponentPromotion = | 
|  | std::conditional_t<(kMaxExponent<Lhs> > kMaxExponent<Rhs>), Lhs, Rhs>; | 
|  |  | 
|  | // Determines the type that can represent the lowest arithmetic value. | 
|  | template <typename Lhs, typename Rhs> | 
|  | using LowestValuePromotion = std::conditional_t< | 
|  | std::is_signed_v<Lhs> | 
|  | ? (!std::is_signed_v<Rhs> || kMaxExponent<Lhs> > kMaxExponent<Rhs>) | 
|  | : (!std::is_signed_v<Rhs> && kMaxExponent<Lhs> < kMaxExponent<Rhs>), | 
|  | Lhs, | 
|  | Rhs>; | 
|  |  | 
|  | // Determines the type that is best able to represent an arithmetic result. | 
|  |  | 
|  | // Default case, used when the side with the max exponent is big enough. | 
|  | template <typename Lhs, | 
|  | typename Rhs = Lhs, | 
|  | bool is_intmax_type = | 
|  | std::is_integral_v<MaxExponentPromotion<Lhs, Rhs>> && | 
|  | kIntegerBitsPlusSign<MaxExponentPromotion<Lhs, Rhs>> == | 
|  | kIntegerBitsPlusSign<intmax_t>, | 
|  | bool is_max_exponent = | 
|  | kStaticDstRangeRelationToSrcRange<MaxExponentPromotion<Lhs, Rhs>, | 
|  | Lhs> == | 
|  | NumericRangeRepresentation::kContained && | 
|  | kStaticDstRangeRelationToSrcRange<MaxExponentPromotion<Lhs, Rhs>, | 
|  | Rhs> == | 
|  | NumericRangeRepresentation::kContained> | 
|  | struct BigEnoughPromotionImpl { | 
|  | using type = MaxExponentPromotion<Lhs, Rhs>; | 
|  | static constexpr bool kContained = true; | 
|  | }; | 
|  |  | 
|  | // We can use a twice wider type to fit. | 
|  | template <typename Lhs, typename Rhs> | 
|  | struct BigEnoughPromotionImpl<Lhs, Rhs, false, false> { | 
|  | using type = | 
|  | TwiceWiderInteger<MaxExponentPromotion<Lhs, Rhs>, | 
|  | std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>; | 
|  | static constexpr bool kContained = true; | 
|  | }; | 
|  |  | 
|  | // No type is large enough. | 
|  | template <typename Lhs, typename Rhs> | 
|  | struct BigEnoughPromotionImpl<Lhs, Rhs, true, false> { | 
|  | using type = MaxExponentPromotion<Lhs, Rhs>; | 
|  | static constexpr bool kContained = false; | 
|  | }; | 
|  |  | 
|  | template <typename Lhs, typename Rhs> | 
|  | using BigEnoughPromotion = BigEnoughPromotionImpl<Lhs, Rhs>::type; | 
|  |  | 
|  | template <typename Lhs, typename Rhs> | 
|  | inline constexpr bool kIsBigEnoughPromotionContained = | 
|  | BigEnoughPromotionImpl<Lhs, Rhs>::kContained; | 
|  |  | 
|  | // We can statically check if operations on the provided types can wrap, so we | 
|  | // can skip the checked operations if they're not needed. So, for an integer we | 
|  | // care if the destination type preserves the sign and is twice the width of | 
|  | // the source. | 
|  | template <typename T, typename Lhs, typename Rhs = Lhs> | 
|  | inline constexpr bool kIsIntegerArithmeticSafe = | 
|  | !std::is_floating_point_v<T> && !std::is_floating_point_v<Lhs> && | 
|  | !std::is_floating_point_v<Rhs> && | 
|  | std::is_signed_v<T> >= std::is_signed_v<Lhs> && | 
|  | kIntegerBitsPlusSign<T> >= | 
|  | (2 * kIntegerBitsPlusSign<Lhs>)&&std::is_signed_v<T> >= | 
|  | std::is_signed_v<Rhs> && | 
|  | kIntegerBitsPlusSign<T> >= (2 * kIntegerBitsPlusSign<Rhs>); | 
|  |  | 
|  | // Promotes to a type that can represent any possible result of a binary | 
|  | // arithmetic operation with the source types. | 
|  | template <typename Lhs, typename Rhs> | 
|  | struct FastIntegerArithmeticPromotionImpl { | 
|  | using type = BigEnoughPromotion<Lhs, Rhs>; | 
|  | static constexpr bool kContained = false; | 
|  | }; | 
|  |  | 
|  | template <typename Lhs, typename Rhs> | 
|  | requires(kIsIntegerArithmeticSafe< | 
|  | std::conditional_t<std::is_signed_v<Lhs> || std::is_signed_v<Rhs>, | 
|  | intmax_t, | 
|  | uintmax_t>, | 
|  | MaxExponentPromotion<Lhs, Rhs>>) | 
|  | struct FastIntegerArithmeticPromotionImpl<Lhs, Rhs> { | 
|  | using type = | 
|  | TwiceWiderInteger<MaxExponentPromotion<Lhs, Rhs>, | 
|  | std::is_signed_v<Lhs> || std::is_signed_v<Rhs>>; | 
|  | static_assert(kIsIntegerArithmeticSafe<type, Lhs, Rhs>); | 
|  | static constexpr bool kContained = true; | 
|  | }; | 
|  |  | 
|  | template <typename Lhs, typename Rhs> | 
|  | using FastIntegerArithmeticPromotion = | 
|  | FastIntegerArithmeticPromotionImpl<Lhs, Rhs>::type; | 
|  |  | 
|  | template <typename Lhs, typename Rhs> | 
|  | inline constexpr bool kIsFastIntegerArithmeticPromotionContained = | 
|  | FastIntegerArithmeticPromotionImpl<Lhs, Rhs>::kContained; | 
|  |  | 
|  | template <typename T> | 
|  | struct ArithmeticOrIntegralConstant { | 
|  | using type = T; | 
|  | }; | 
|  |  | 
|  | template <typename T> | 
|  | requires IntegralConstantLike<T> | 
|  | struct ArithmeticOrIntegralConstant<T> { | 
|  | using type = T::value_type; | 
|  | }; | 
|  |  | 
|  | // Extracts the underlying type from an enum. | 
|  | template <typename T> | 
|  | using ArithmeticOrUnderlyingEnum = | 
|  | typename std::conditional_t<std::is_enum_v<T>, | 
|  | std::underlying_type<T>, | 
|  | ArithmeticOrIntegralConstant<T>>::type; | 
|  |  | 
|  | // The following are helper templates used in the CheckedNumeric class. | 
|  | template <typename T> | 
|  | requires std::is_arithmetic_v<T> | 
|  | class CheckedNumeric; | 
|  |  | 
|  | template <typename T> | 
|  | requires std::is_arithmetic_v<T> | 
|  | class ClampedNumeric; | 
|  |  | 
|  | template <typename T> | 
|  | requires std::is_arithmetic_v<T> | 
|  | class StrictNumeric; | 
|  |  | 
|  | // Used to treat CheckedNumeric and arithmetic underlying types the same. | 
|  | template <typename T> | 
|  | inline constexpr bool kIsCheckedNumeric = false; | 
|  | template <typename T> | 
|  | inline constexpr bool kIsCheckedNumeric<CheckedNumeric<T>> = true; | 
|  | template <typename T> | 
|  | concept IsCheckedNumeric = kIsCheckedNumeric<T>; | 
|  |  | 
|  | template <typename T> | 
|  | inline constexpr bool kIsClampedNumeric = false; | 
|  | template <typename T> | 
|  | inline constexpr bool kIsClampedNumeric<ClampedNumeric<T>> = true; | 
|  | template <typename T> | 
|  | concept IsClampedNumeric = kIsClampedNumeric<T>; | 
|  |  | 
|  | template <typename T> | 
|  | inline constexpr bool kIsStrictNumeric = false; | 
|  | template <typename T> | 
|  | inline constexpr bool kIsStrictNumeric<StrictNumeric<T>> = true; | 
|  | template <typename T> | 
|  | concept IsStrictNumeric = kIsStrictNumeric<T>; | 
|  |  | 
|  | template <typename T> | 
|  | struct UnderlyingTypeImpl { | 
|  | using type = ArithmeticOrUnderlyingEnum<T>; | 
|  | }; | 
|  | template <typename T> | 
|  | struct UnderlyingTypeImpl<CheckedNumeric<T>> { | 
|  | using type = T; | 
|  | }; | 
|  | template <typename T> | 
|  | struct UnderlyingTypeImpl<ClampedNumeric<T>> { | 
|  | using type = T; | 
|  | }; | 
|  | template <typename T> | 
|  | struct UnderlyingTypeImpl<StrictNumeric<T>> { | 
|  | using type = T; | 
|  | }; | 
|  | template <typename T> | 
|  | using UnderlyingType = UnderlyingTypeImpl<T>::type; | 
|  |  | 
|  | template <typename T> | 
|  | inline constexpr bool kIsNumeric = std::is_arithmetic_v<UnderlyingType<T>>; | 
|  | template <typename T> | 
|  | requires(IsCheckedNumeric<T> || IsClampedNumeric<T> || IsStrictNumeric<T>) | 
|  | inline constexpr bool kIsNumeric<T> = true; | 
|  | template <typename T> | 
|  | concept IsNumeric = kIsNumeric<T>; | 
|  |  | 
|  | template <typename L, typename R> | 
|  | concept IsCheckedOp = (IsCheckedNumeric<L> && IsNumeric<R>) || | 
|  | (IsCheckedNumeric<R> && IsNumeric<L>); | 
|  |  | 
|  | template <typename L, typename R> | 
|  | concept IsClampedOp = | 
|  | !IsCheckedOp<L, R> && ((IsClampedNumeric<L> && IsNumeric<R>) || | 
|  | (IsClampedNumeric<R> && IsNumeric<L>)); | 
|  |  | 
|  | template <typename L, typename R> | 
|  | concept IsStrictOp = !IsCheckedOp<L, R> && !IsClampedOp<L, R> && | 
|  | ((IsStrictNumeric<L> && IsNumeric<R>) || | 
|  | (IsStrictNumeric<R> && IsNumeric<L>)); | 
|  |  | 
|  | // as_signed<> returns the supplied integral value (or integral castable | 
|  | // Numeric template) cast as a signed integral of equivalent precision. | 
|  | // I.e. it's mostly an alias for: static_cast<std::make_signed<T>::type>(t) | 
|  | template <typename Src, typename Dst = std::make_signed_t<UnderlyingType<Src>>> | 
|  | requires std::integral<Dst> | 
|  | constexpr auto as_signed(Src value) { | 
|  | return static_cast<Dst>(value); | 
|  | } | 
|  |  | 
|  | // as_unsigned<> returns the supplied integral value (or integral castable | 
|  | // Numeric template) cast as an unsigned integral of equivalent precision. | 
|  | // I.e. it's mostly an alias for: static_cast<std::make_unsigned<T>::type>(t) | 
|  | template <typename Src, | 
|  | typename Dst = std::make_unsigned_t<UnderlyingType<Src>>> | 
|  | requires std::integral<Dst> | 
|  | constexpr auto as_unsigned(Src value) { | 
|  | return static_cast<Dst>(value); | 
|  | } | 
|  |  | 
|  | template <typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | struct IsLess { | 
|  | using SumT = decltype(std::declval<L>() + std::declval<R>()); | 
|  | static constexpr bool Test(L lhs, R rhs) { | 
|  | const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs); | 
|  | const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs); | 
|  | return l_range.IsUnderflow() || r_range.IsOverflow() || | 
|  | (l_range == r_range && | 
|  | static_cast<SumT>(lhs) < static_cast<SumT>(rhs)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | struct IsLessOrEqual { | 
|  | using SumT = decltype(std::declval<L>() + std::declval<R>()); | 
|  | static constexpr bool Test(L lhs, R rhs) { | 
|  | const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs); | 
|  | const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs); | 
|  | return l_range.IsUnderflow() || r_range.IsOverflow() || | 
|  | (l_range == r_range && | 
|  | static_cast<SumT>(lhs) <= static_cast<SumT>(rhs)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | struct IsGreater { | 
|  | using SumT = decltype(std::declval<L>() + std::declval<R>()); | 
|  | static constexpr bool Test(L lhs, R rhs) { | 
|  | const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs); | 
|  | const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs); | 
|  | return l_range.IsOverflow() || r_range.IsUnderflow() || | 
|  | (l_range == r_range && | 
|  | static_cast<SumT>(lhs) > static_cast<SumT>(rhs)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | struct IsGreaterOrEqual { | 
|  | using SumT = decltype(std::declval<L>() + std::declval<R>()); | 
|  | static constexpr bool Test(L lhs, R rhs) { | 
|  | const RangeCheck l_range = DstRangeRelationToSrcRange<R>(lhs); | 
|  | const RangeCheck r_range = DstRangeRelationToSrcRange<L>(rhs); | 
|  | return l_range.IsOverflow() || r_range.IsUnderflow() || | 
|  | (l_range == r_range && | 
|  | static_cast<SumT>(lhs) >= static_cast<SumT>(rhs)); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | struct IsEqual { | 
|  | using SumT = decltype(std::declval<L>() + std::declval<R>()); | 
|  | static constexpr bool Test(L lhs, R rhs) { | 
|  | return DstRangeRelationToSrcRange<R>(lhs) == | 
|  | DstRangeRelationToSrcRange<L>(rhs) && | 
|  | static_cast<SumT>(lhs) == static_cast<SumT>(rhs); | 
|  | } | 
|  | }; | 
|  |  | 
|  | template <typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | struct IsNotEqual { | 
|  | using SumT = decltype(std::declval<L>() + std::declval<R>()); | 
|  | static constexpr bool Test(L lhs, R rhs) { | 
|  | return DstRangeRelationToSrcRange<R>(lhs) != | 
|  | DstRangeRelationToSrcRange<L>(rhs) || | 
|  | static_cast<SumT>(lhs) != static_cast<SumT>(rhs); | 
|  | } | 
|  | }; | 
|  |  | 
|  | // These perform the actual math operations on the CheckedNumerics. | 
|  | // Binary arithmetic operations. | 
|  | template <template <typename, typename> typename C, typename L, typename R> | 
|  | requires std::is_arithmetic_v<L> && std::is_arithmetic_v<R> | 
|  | constexpr bool SafeCompare(L lhs, R rhs) { | 
|  | using BigType = BigEnoughPromotion<L, R>; | 
|  | return kIsBigEnoughPromotionContained<L, R> | 
|  | // Force to a larger type for speed if both are contained. | 
|  | ? C<BigType, BigType>::Test(static_cast<BigType>(lhs), | 
|  | static_cast<BigType>(rhs)) | 
|  | // Let the template functions figure it out for mixed types. | 
|  | : C<L, R>::Test(lhs, rhs); | 
|  | } | 
|  |  | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr bool kIsMaxInRangeForNumericType = | 
|  | IsGreaterOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::max(), | 
|  | std::numeric_limits<Src>::max()); | 
|  |  | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr bool kIsMinInRangeForNumericType = | 
|  | IsLessOrEqual<Dst, Src>::Test(std::numeric_limits<Dst>::lowest(), | 
|  | std::numeric_limits<Src>::lowest()); | 
|  |  | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr Dst kCommonMax = | 
|  | kIsMaxInRangeForNumericType<Dst, Src> | 
|  | ? static_cast<Dst>(std::numeric_limits<Src>::max()) | 
|  | : std::numeric_limits<Dst>::max(); | 
|  |  | 
|  | template <typename Dst, typename Src> | 
|  | inline constexpr Dst kCommonMin = | 
|  | kIsMinInRangeForNumericType<Dst, Src> | 
|  | ? static_cast<Dst>(std::numeric_limits<Src>::lowest()) | 
|  | : std::numeric_limits<Dst>::lowest(); | 
|  |  | 
|  | // This is a wrapper to generate return the max or min for a supplied type. | 
|  | // If the argument is false, the returned value is the maximum. If true the | 
|  | // returned value is the minimum. | 
|  | template <typename Dst, typename Src = Dst> | 
|  | constexpr Dst CommonMaxOrMin(bool is_min) { | 
|  | return is_min ? kCommonMin<Dst, Src> : kCommonMax<Dst, Src>; | 
|  | } | 
|  |  | 
|  | }  // namespace base::internal | 
|  |  | 
|  | #endif  // BASE_NUMERICS_SAFE_CONVERSIONS_IMPL_H_ |