tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_